diff --git a/pkg/proto/snapshot_types.go b/pkg/proto/snapshot_types.go index d814b94a9..07a7d3112 100644 --- a/pkg/proto/snapshot_types.go +++ b/pkg/proto/snapshot_types.go @@ -13,7 +13,6 @@ type AtomicSnapshot interface { Apply(SnapshotApplier) error /* TODO remove it. It is temporarily used to mark snapshots generated by tx diff that shouldn't be applied, because balances diffs are applied later in the block. */ - IsGeneratedByTxDiff() bool AppendToProtobuf(txSnapshots *g.TransactionStateSnapshot) error } type WavesBalanceSnapshot struct { @@ -21,10 +20,6 @@ type WavesBalanceSnapshot struct { Balance uint64 } -func (s WavesBalanceSnapshot) IsGeneratedByTxDiff() bool { - return true -} - func (s WavesBalanceSnapshot) Apply(a SnapshotApplier) error { return a.ApplyWavesBalance(s) } func (s WavesBalanceSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_Balance, error) { @@ -70,10 +65,6 @@ type AssetBalanceSnapshot struct { Balance uint64 } -func (s AssetBalanceSnapshot) IsGeneratedByTxDiff() bool { - return true -} - func (s AssetBalanceSnapshot) Apply(a SnapshotApplier) error { return a.ApplyAssetBalance(s) } func (s AssetBalanceSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_Balance, error) { @@ -119,10 +110,6 @@ type DataEntriesSnapshot struct { // AccountData in pb DataEntries []DataEntry } -func (s DataEntriesSnapshot) IsGeneratedByTxDiff() bool { - return false -} - func (s DataEntriesSnapshot) Apply(a SnapshotApplier) error { return a.ApplyDataEntries(s) } func (s DataEntriesSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_AccountData, error) { @@ -169,10 +156,6 @@ type AccountScriptSnapshot struct { VerifierComplexity uint64 } -func (s AccountScriptSnapshot) IsGeneratedByTxDiff() bool { - return false -} - func (s AccountScriptSnapshot) Apply(a SnapshotApplier) error { return a.ApplyAccountScript(s) } func (s AccountScriptSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_AccountScript, error) { @@ -220,10 +203,6 @@ type AssetScriptSnapshot struct { Script Script } -func (s AssetScriptSnapshot) IsGeneratedByTxDiff() bool { - return false -} - func (s AssetScriptSnapshot) Apply(a SnapshotApplier) error { return a.ApplyAssetScript(s) } func (s AssetScriptSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_AssetScript, error) { @@ -266,10 +245,6 @@ type LeaseBalanceSnapshot struct { LeaseOut uint64 } -func (s LeaseBalanceSnapshot) IsGeneratedByTxDiff() bool { - return true -} - func (s LeaseBalanceSnapshot) Apply(a SnapshotApplier) error { return a.ApplyLeaseBalance(s) } func (s LeaseBalanceSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_LeaseBalance, error) { @@ -330,10 +305,6 @@ type NewLeaseSnapshot struct { RecipientAddr WavesAddress } -func (s NewLeaseSnapshot) IsGeneratedByTxDiff() bool { - return false -} - func (s NewLeaseSnapshot) Apply(a SnapshotApplier) error { return a.ApplyNewLease(s) } func (s NewLeaseSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_NewLease, error) { @@ -385,10 +356,6 @@ type CancelledLeaseSnapshot struct { func (s CancelledLeaseSnapshot) Apply(a SnapshotApplier) error { return a.ApplyCancelledLease(s) } -func (s CancelledLeaseSnapshot) IsGeneratedByTxDiff() bool { - return false -} - func (s CancelledLeaseSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_CancelledLease, error) { return &g.TransactionStateSnapshot_CancelledLease{ LeaseId: s.LeaseID.Bytes(), @@ -419,10 +386,6 @@ type SponsorshipSnapshot struct { MinSponsoredFee uint64 } -func (s SponsorshipSnapshot) IsGeneratedByTxDiff() bool { - return false -} - func (s SponsorshipSnapshot) Apply(a SnapshotApplier) error { return a.ApplySponsorship(s) } func (s SponsorshipSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_Sponsorship, error) { @@ -461,10 +424,6 @@ type AliasSnapshot struct { Alias Alias } -func (s AliasSnapshot) IsGeneratedByTxDiff() bool { - return false -} - func (s AliasSnapshot) Apply(a SnapshotApplier) error { return a.ApplyAlias(s) } func (s AliasSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_Alias, error) { @@ -503,10 +462,6 @@ type FilledVolumeFeeSnapshot struct { // OrderFill FilledFee uint64 } -func (s FilledVolumeFeeSnapshot) IsGeneratedByTxDiff() bool { - return false -} - func (s FilledVolumeFeeSnapshot) Apply(a SnapshotApplier) error { return a.ApplyFilledVolumeAndFee(s) } func (s FilledVolumeFeeSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_OrderFill, error) { @@ -553,10 +508,6 @@ type NewAssetSnapshot struct { IsNFT bool } -func (s NewAssetSnapshot) IsGeneratedByTxDiff() bool { - return false -} - func (s NewAssetSnapshot) Apply(a SnapshotApplier) error { return a.ApplyNewAsset(s) } func (s NewAssetSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_NewAsset, error) { @@ -604,10 +555,6 @@ type AssetVolumeSnapshot struct { // AssetVolume in pb IsReissuable bool } -func (s AssetVolumeSnapshot) IsGeneratedByTxDiff() bool { - return false -} - func (s AssetVolumeSnapshot) Apply(a SnapshotApplier) error { return a.ApplyAssetVolume(s) } func (s AssetVolumeSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_AssetVolume, error) { @@ -646,10 +593,6 @@ type AssetDescriptionSnapshot struct { // AssetNameAndDescription in pb AssetDescription string } -func (s AssetDescriptionSnapshot) IsGeneratedByTxDiff() bool { - return false -} - func (s AssetDescriptionSnapshot) Apply(a SnapshotApplier) error { return a.ApplyAssetDescription(s) } func (s AssetDescriptionSnapshot) ToProtobuf() (*g.TransactionStateSnapshot_AssetNameAndDescription, error) { @@ -690,10 +633,6 @@ func (s TransactionStatusSnapshot) Apply(a SnapshotApplier) error { return a.ApplyTransactionsStatus(s) } -func (s TransactionStatusSnapshot) IsGeneratedByTxDiff() bool { - return false -} - func (s *TransactionStatusSnapshot) FromProtobuf(p g.TransactionStatus) error { switch p { case g.TransactionStatus_SUCCEEDED: diff --git a/pkg/state/appender.go b/pkg/state/appender.go index 0cb65417a..4c5f5318a 100644 --- a/pkg/state/appender.go +++ b/pkg/state/appender.go @@ -349,16 +349,22 @@ func (a *txAppender) commitTxApplication( proto.MustAddressFromPublicKey(a.settings.AddressSchemeCharacter, params.currentMinerPK), applicationRes.checkerData, ) - diff = applicationRes.changes.diff applicationStatus = applicationRes.status ) - snapshot, err := a.txHandler.performTx(tx, pi, params.validatingUtx, invocationRes, applicationStatus, diff) + balanceChanges := applicationRes.changes.diff.balancesChanges() + err = a.diffApplier.validateBalancesChanges(balanceChanges) + if err != nil { + return txSnapshot{}, err + } + a.diffStor.reset() + snapshot, err := a.txHandler.performTx(tx, pi, params.validatingUtx, invocationRes, applicationStatus, balanceChanges) if err != nil { return txSnapshot{}, wrapErr(TxCommitmentError, errors.Wrapf(err, "failed to perform transaction %q", base58.Encode(txID)), ) } + if !params.validatingUtx { // TODO: snapshots for miner fee should be generated here, but not saved // They must be saved in snapshot applier @@ -591,7 +597,6 @@ func (a *txAppender) appendTx(tx proto.Transaction, params *appendTxParams) (txS } // invocationResult may be empty if it was not an Invoke Transaction - snapshot, err := a.commitTxApplication(tx, params, invocationResult, applicationRes) if err != nil { @@ -607,29 +612,6 @@ func (a *txAppender) appendTx(tx proto.Transaction, params *appendTxParams) (txS return snapshot, nil } -// rewards and 60% of the fee to the previous miner. -func (a *txAppender) createInitialBlockSnapshot(minerAndRewardDiff txDiff) (txSnapshot, error) { - addrWavesBalanceDiff, _, err := balanceDiffFromTxDiff(minerAndRewardDiff, a.settings.AddressSchemeCharacter) - if err != nil { - return txSnapshot{}, errors.Wrap(err, "failed to create balance diff from tx diff") - } - // add miner address to the diff - var snapshot txSnapshot - for wavesAddress, diffAmount := range addrWavesBalanceDiff { - var fullBalance balanceProfile - fullBalance, err = a.stor.balances.newestWavesBalance(wavesAddress.ID()) - if err != nil { - return txSnapshot{}, errors.Wrap(err, "failed to receive sender's waves balance") - } - newBalance := &proto.WavesBalanceSnapshot{ - Address: wavesAddress, - Balance: uint64(int64(fullBalance.balance) + diffAmount.balance), - } - snapshot.regular = append(snapshot.regular, newBalance) - } - return snapshot, nil -} - func calculateInitialSnapshotStateHash( h *txSnapshotHasher, blockHasParent bool, @@ -693,8 +675,9 @@ func (a *txAppender) appendBlock(params *appendBlockParams) error { if err != nil { return err } + // create the initial snapshot - initialSnapshot, err := a.createInitialBlockSnapshot(minerAndRewardDiff) + initialSnapshot, err := a.txHandler.tp.createInitialBlockSnapshot(minerAndRewardDiff.balancesChanges()) if err != nil { return errors.Wrap(err, "failed to create initial snapshot") } @@ -712,6 +695,21 @@ func (a *txAppender) appendBlock(params *appendBlockParams) error { } defer hasher.Release() + // Save miner diff first (for validation) + if err = a.diffStor.saveTxDiff(minerAndRewardDiff); err != nil { + return err + } + err = a.diffApplier.validateBalancesChanges(minerAndRewardDiff.balancesChanges()) + if err != nil { + return errors.Wrap(err, "failed to validate miner reward changes") + } + a.diffStor.reset() + + err = initialSnapshot.ApplyInitialSnapshot(a.txHandler.sa) + if err != nil { + return errors.Wrap(err, "failed to apply an initial snapshot") + } + // get initial snapshot hash for block stateHash, err := calculateInitialSnapshotStateHash( hasher, @@ -726,13 +724,6 @@ func (a *txAppender) appendBlock(params *appendBlockParams) error { ) } - // TODO apply this snapshot when balances are refatored - // err = initialSnapshot.Apply(&snapshotApplier) - - // Save miner diff first (for validation) - if err = a.diffStor.saveTxDiff(minerAndRewardDiff); err != nil { - return err - } blockV5Activated, err := a.stor.features.newestIsActivated(int16(settings.BlockV5)) if err != nil { return err @@ -794,10 +785,14 @@ func (a *txAppender) appendBlock(params *appendBlockParams) error { if ssErr := a.stor.snapshots.saveSnapshots(params.block.BlockID(), currentBlockHeight, bs); ssErr != nil { return ssErr } + + // clean up legacy state hash records with zero diffs + a.stor.balances.filterZeroDiffsSHOut(blockID) // TODO: check snapshot hash with the block snapshot hash if it exists if shErr := a.stor.stateHashes.saveSnapshotStateHash(stateHash, currentBlockHeight, blockID); shErr != nil { return errors.Wrapf(shErr, "failed to save block shasnpt hash at height %d", currentBlockHeight) } + // Save fee distribution of this block. // This will be needed for createMinerAndRewardDiff() of next block due to NG. return a.blockDiffer.saveCurFeeDistr(params.block) diff --git a/pkg/state/assets.go b/pkg/state/assets.go index 3aa4dd256..b3437ba2c 100644 --- a/pkg/state/assets.go +++ b/pkg/state/assets.go @@ -313,7 +313,8 @@ type assetInfoChange struct { } func (a *assets) updateAssetInfo(asset crypto.Digest, ch *assetInfoChange, blockID proto.BlockID) error { - info, err := a.newestChangeableInfo(asset) + assetID := proto.AssetIDFromDigest(asset) + info, err := a.newestChangeableInfo(assetID) if err != nil { return errors.Errorf("failed to get asset info: %v\n", err) } @@ -321,7 +322,7 @@ func (a *assets) updateAssetInfo(asset crypto.Digest, ch *assetInfoChange, block info.description = ch.newDescription info.lastNameDescChangeHeight = ch.newHeight record := &assetHistoryRecord{assetChangeableInfo: *info} - return a.addNewRecord(proto.AssetIDFromDigest(asset), record, blockID) + return a.addNewRecord(assetID, record, blockID) } func (a *assets) newestLastUpdateHeight(assetID proto.AssetID) (uint64, error) { @@ -355,8 +356,7 @@ func (a *assets) newestConstInfo(assetID proto.AssetID) (*assetConstInfo, error) return a.constInfo(assetID) } -func (a *assets) newestChangeableInfo(asset crypto.Digest) (*assetChangeableInfo, error) { - assetID := proto.AssetIDFromDigest(asset) +func (a *assets) newestChangeableInfo(assetID proto.AssetID) (*assetChangeableInfo, error) { if info, ok := a.uncertainAssetInfo[assetID]; ok { return &info.assetInfo.assetChangeableInfo, nil } @@ -391,7 +391,7 @@ func (a *assets) newestAssetInfo(assetID proto.AssetID) (*assetInfo, error) { if err != nil { return nil, err } - changeableInfo, err := a.newestChangeableInfo(proto.ReconstructDigest(assetID, constInfo.tail)) + changeableInfo, err := a.newestChangeableInfo(assetID) if err != nil { return nil, err } diff --git a/pkg/state/balances.go b/pkg/state/balances.go index 08b3bc85b..0d5878e4d 100644 --- a/pkg/state/balances.go +++ b/pkg/state/balances.go @@ -168,7 +168,7 @@ func (ac *assetRecordForHashes) writeTo(w io.Writer) error { type assetInfoGetter interface { assetInfo(assetID proto.AssetID) (*assetInfo, error) - newestAssetInfo(assetID proto.AssetID) (*assetInfo, error) + newestConstInfo(assetID proto.AssetID) (*assetConstInfo, error) } type balances struct { @@ -187,6 +187,11 @@ type balances struct { calculateHashes bool scheme proto.Scheme + + // used for legacy state hashes to filter out statehash temporary records with 0 change in a block. + wavesDiffRecordsLegacySH wavesDiffRecordsLegacyStateHash + assetDiffRecordsLegacySH assetDiffRecordsLegacyStateHash + leasesDiffRecordsLegacySH leasesRecordLegacyStateHash } func newBalances(db keyvalue.IterableKeyVal, hs *historyStorage, assets assetInfoGetter, scheme proto.Scheme, calcHashes bool) (*balances, error) { @@ -207,6 +212,10 @@ func newBalances(db keyvalue.IterableKeyVal, hs *historyStorage, assets assetInf assetsHashes: make(map[proto.BlockID]crypto.Digest), leaseHashesState: make(map[proto.BlockID]*stateForHashes), leaseHashes: make(map[proto.BlockID]crypto.Digest), + + wavesDiffRecordsLegacySH: wavesDiffRecordsLegacyStateHash{make(map[string]int64)}, + assetDiffRecordsLegacySH: assetDiffRecordsLegacyStateHash{make(map[string]int64)}, + leasesDiffRecordsLegacySH: leasesRecordLegacyStateHash{make(map[string]leaseDiffRecord)}, }, nil } @@ -234,6 +243,27 @@ func (s *balances) leaseHashAt(blockID proto.BlockID) crypto.Digest { return hash } +func (s *balances) addAssetBalanceChangeLegacySH(addr proto.WavesAddress, asset proto.AssetID, balanceDiff int64) { + if !s.calculateHashes { + return + } + key := assetBalanceKey{address: addr.ID(), asset: asset} + keyBytes := key.bytes() + keyStr := string(keyBytes) + s.assetDiffRecordsLegacySH.add(balanceDiff, keyStr) +} + +func (s *balances) addWavesBalanceChangeLegacySH(addr proto.WavesAddress, change balanceDiff) { + if !s.calculateHashes { + return + } + key := wavesBalanceKey{address: addr.ID()} + keyBytes := key.bytes() + keyStr := string(keyBytes) + s.wavesDiffRecordsLegacySH.add(change.balance, keyStr) + s.leasesDiffRecordsLegacySH.add(change.leaseIn, change.leaseOut, keyStr) +} + func (s *balances) cancelAllLeases(blockID proto.BlockID) error { iter, err := s.hs.newNewestTopEntryIterator(wavesBalance) if err != nil { @@ -596,6 +626,29 @@ func (s *balances) wavesBalance(addr proto.AddressID) (balanceProfile, error) { return r.balanceProfile, nil } +func (s *balances) calculateStateHashesAssetBalance(addr proto.AddressID, assetID proto.AssetID, + balance uint64, blockID proto.BlockID, keyStr string) error { + info, err := s.assets.newestConstInfo(assetID) + if err != nil { + return err + } + wavesAddress, err := addr.ToWavesAddress(s.scheme) + if err != nil { + return err + } + fullAssetID := proto.ReconstructDigest(assetID, info.tail) + ac := &assetRecordForHashes{ + addr: &wavesAddress, + asset: fullAssetID, + balance: balance, + } + if _, ok := s.assetsHashesState[blockID]; !ok { + s.assetsHashesState[blockID] = newStateForHashes() + } + s.assetsHashesState[blockID].set(keyStr, ac) + return nil +} + func (s *balances) setAssetBalance(addr proto.AddressID, assetID proto.AssetID, balance uint64, blockID proto.BlockID) error { key := assetBalanceKey{address: addr, asset: assetID} keyBytes := key.bytes() @@ -606,26 +659,160 @@ func (s *balances) setAssetBalance(addr proto.AddressID, assetID proto.AssetID, return err } if s.calculateHashes { - info, err := s.assets.newestAssetInfo(assetID) - if err != nil { - return err + shErr := s.calculateStateHashesAssetBalance(addr, assetID, balance, blockID, keyStr) + if shErr != nil { + return shErr } - wavesAddress, err := addr.ToWavesAddress(s.scheme) - if err != nil { - return err + } + return s.hs.addNewEntry(assetBalance, keyBytes, recordBytes, blockID) +} + +func (s *balances) filterZeroWavesDiffRecords(blockID proto.BlockID) { + for key, diffWavesRec := range s.wavesDiffRecordsLegacySH.wavesDiffRecordsLegacySHs { + if diffWavesRec == 0 { + temporarySHRecords, ok := s.wavesHashesState[blockID] + if ok && temporarySHRecords != nil { + temporarySHRecords.remove(key) + s.wavesHashesState[blockID] = temporarySHRecords + } } - fullAssetID := proto.ReconstructDigest(assetID, info.tail) - ac := &assetRecordForHashes{ + } +} + +func (s *balances) filterZeroAssetDiffRecords(blockID proto.BlockID) { + for key, assetDiffRec := range s.assetDiffRecordsLegacySH.assetDiffRecordsLegacySHs { + if assetDiffRec == 0 { + temporarySHRecords, ok := s.assetsHashesState[blockID] + if ok && temporarySHRecords != nil { + temporarySHRecords.remove(key) + s.assetsHashesState[blockID] = temporarySHRecords + } + } + } +} + +func (s *balances) filterZeroLeasingDiffRecords(blockID proto.BlockID) { + for key, leaseDiffRec := range s.leasesDiffRecordsLegacySH.leaseDiffRecordsLegacySH { + if leaseDiffRec.leaseInDiff == 0 && leaseDiffRec.leaseOutDiff == 0 { + temporarySHRecords, ok := s.leaseHashesState[blockID] + if ok && temporarySHRecords != nil { + temporarySHRecords.remove(key) + s.leaseHashesState[blockID] = temporarySHRecords + } + } + } +} + +func (s *balances) filterZeroDiffsSHOut(blockID proto.BlockID) { + if !s.calculateHashes { + return + } + s.filterZeroWavesDiffRecords(blockID) + s.filterZeroAssetDiffRecords(blockID) + s.filterZeroLeasingDiffRecords(blockID) + + s.wavesDiffRecordsLegacySH.reset() + s.assetDiffRecordsLegacySH.reset() + s.leasesDiffRecordsLegacySH.reset() +} + +type wavesDiffRecordsLegacyStateHash struct { + wavesDiffRecordsLegacySHs map[string]int64 +} + +func (w *wavesDiffRecordsLegacyStateHash) add(diff int64, keyStr string) { + if diff == 0 { + return + } + prevDiff, ok := w.wavesDiffRecordsLegacySHs[keyStr] + if ok { + newDiff := prevDiff + diff + w.wavesDiffRecordsLegacySHs[keyStr] = newDiff + } else { + w.wavesDiffRecordsLegacySHs[keyStr] = diff + } +} + +func (w *wavesDiffRecordsLegacyStateHash) reset() { + w.wavesDiffRecordsLegacySHs = make(map[string]int64) +} + +type assetDiffRecordsLegacyStateHash struct { + assetDiffRecordsLegacySHs map[string]int64 +} + +func (w *assetDiffRecordsLegacyStateHash) add(diff int64, keyStr string) { + if diff == 0 { + return + } + prevDiff, ok := w.assetDiffRecordsLegacySHs[keyStr] + if ok { + newDiff := prevDiff + diff + w.assetDiffRecordsLegacySHs[keyStr] = newDiff + } else { + w.assetDiffRecordsLegacySHs[keyStr] = diff + } +} + +func (w *assetDiffRecordsLegacyStateHash) reset() { + w.assetDiffRecordsLegacySHs = make(map[string]int64) +} + +type leaseDiffRecord struct { + leaseInDiff int64 + leaseOutDiff int64 +} + +type leasesRecordLegacyStateHash struct { + leaseDiffRecordsLegacySH map[string]leaseDiffRecord +} + +func (w *leasesRecordLegacyStateHash) add(diffLeaseIn int64, diffLeaseOut int64, keyStr string) { + if diffLeaseIn == 0 && diffLeaseOut == 0 { + return + } + prevDiffLeaseInOut, ok := w.leaseDiffRecordsLegacySH[keyStr] + if ok { + newDiffLeaseIn := prevDiffLeaseInOut.leaseInDiff + diffLeaseIn + newDiffLeaseOut := prevDiffLeaseInOut.leaseOutDiff + diffLeaseOut + w.leaseDiffRecordsLegacySH[keyStr] = leaseDiffRecord{leaseInDiff: newDiffLeaseIn, leaseOutDiff: newDiffLeaseOut} + } else { + w.leaseDiffRecordsLegacySH[keyStr] = leaseDiffRecord{leaseInDiff: diffLeaseIn, leaseOutDiff: diffLeaseOut} + } +} + +func (w *leasesRecordLegacyStateHash) reset() { + w.leaseDiffRecordsLegacySH = make(map[string]leaseDiffRecord) +} + +func (s *balances) calculateStateHashesWavesBalance(addr proto.AddressID, balance wavesValue, + blockID proto.BlockID, keyStr string, record wavesBalanceRecord) error { + wavesAddress, err := addr.ToWavesAddress(s.scheme) + if err != nil { + return err + } + if balance.balanceChange { + wc := &wavesRecordForHashes{ addr: &wavesAddress, - asset: fullAssetID, - balance: balance, + balance: record.balance, } - if _, ok := s.assetsHashesState[blockID]; !ok { - s.assetsHashesState[blockID] = newStateForHashes() + if _, ok := s.wavesHashesState[blockID]; !ok { + s.wavesHashesState[blockID] = newStateForHashes() } - s.assetsHashesState[blockID].set(keyStr, ac) + s.wavesHashesState[blockID].set(keyStr, wc) } - return s.hs.addNewEntry(assetBalance, keyBytes, recordBytes, blockID) + if balance.leaseChange { + lc := &leaseBalanceRecordForHashes{ + addr: &wavesAddress, + leaseIn: record.leaseIn, + leaseOut: record.leaseOut, + } + if _, ok := s.leaseHashesState[blockID]; !ok { + s.leaseHashesState[blockID] = newStateForHashes() + } + s.leaseHashesState[blockID].set(keyStr, lc) + } + return nil } func (s *balances) setWavesBalance(addr proto.AddressID, balance wavesValue, blockID proto.BlockID) error { @@ -638,30 +825,9 @@ func (s *balances) setWavesBalance(addr proto.AddressID, balance wavesValue, blo return err } if s.calculateHashes { - wavesAddress, err := addr.ToWavesAddress(s.scheme) - if err != nil { - return err - } - if balance.balanceChange { - wc := &wavesRecordForHashes{ - addr: &wavesAddress, - balance: record.balance, - } - if _, ok := s.wavesHashesState[blockID]; !ok { - s.wavesHashesState[blockID] = newStateForHashes() - } - s.wavesHashesState[blockID].set(keyStr, wc) - } - if balance.leaseChange { - lc := &leaseBalanceRecordForHashes{ - addr: &wavesAddress, - leaseIn: record.leaseIn, - leaseOut: record.leaseOut, - } - if _, ok := s.leaseHashesState[blockID]; !ok { - s.leaseHashesState[blockID] = newStateForHashes() - } - s.leaseHashesState[blockID].set(keyStr, lc) + shErr := s.calculateStateHashesWavesBalance(addr, balance, blockID, keyStr, record) + if shErr != nil { + return shErr } } return s.hs.addNewEntry(wavesBalance, keyBytes, recordBytes, blockID) diff --git a/pkg/state/snapshot_applier.go b/pkg/state/snapshot_applier.go index 78d4801c5..38d7db370 100644 --- a/pkg/state/snapshot_applier.go +++ b/pkg/state/snapshot_applier.go @@ -76,6 +76,7 @@ func (a *blockSnapshotsApplier) AfterTxSnapshotApply() error { return errors.Wrapf(err, "failed to push state hash for cancelled lease %q", cancelledLeaseID) } } + a.txSnapshotContext = txSnapshotContext{} // reset to default return nil } diff --git a/pkg/state/snapshot_generator.go b/pkg/state/snapshot_generator.go index 0605bc6da..48db07edf 100644 --- a/pkg/state/snapshot_generator.go +++ b/pkg/state/snapshot_generator.go @@ -25,7 +25,7 @@ func newSnapshotGenerator(stor *blockchainEntitiesStorage, scheme proto.Scheme) func (sg *snapshotGenerator) performGenesis( transaction proto.Transaction, _ *performerInfo, _ *invocationResult, - balanceChanges txDiff) (txSnapshot, error) { + balanceChanges []balanceChanges) (txSnapshot, error) { _, ok := transaction.(*proto.Genesis) if !ok { return txSnapshot{}, errors.New("failed to convert interface to genesis transaction") @@ -34,7 +34,7 @@ func (sg *snapshotGenerator) performGenesis( } func (sg *snapshotGenerator) performPayment(transaction proto.Transaction, _ *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { _, ok := transaction.(*proto.Payment) if !ok { return txSnapshot{}, errors.New("failed to convert interface to payment transaction") @@ -43,7 +43,7 @@ func (sg *snapshotGenerator) performPayment(transaction proto.Transaction, _ *pe } func (sg *snapshotGenerator) performTransferWithSig(transaction proto.Transaction, _ *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { _, ok := transaction.(*proto.TransferWithSig) if !ok { return txSnapshot{}, errors.New("failed to convert interface to transfer with sig transaction") @@ -52,7 +52,7 @@ func (sg *snapshotGenerator) performTransferWithSig(transaction proto.Transactio } func (sg *snapshotGenerator) performTransferWithProofs(transaction proto.Transaction, _ *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { _, ok := transaction.(*proto.TransferWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to transfer with proofs transaction") @@ -61,7 +61,7 @@ func (sg *snapshotGenerator) performTransferWithProofs(transaction proto.Transac } func (sg *snapshotGenerator) performIssueWithSig(transaction proto.Transaction, info *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.IssueWithSig) if !ok { return txSnapshot{}, errors.New("failed to convert interface to IssueWithSig transaction") @@ -78,7 +78,7 @@ func (sg *snapshotGenerator) performIssueWithSig(transaction proto.Transaction, } func (sg *snapshotGenerator) performIssueWithProofs(transaction proto.Transaction, info *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.IssueWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to IssueWithProofs transaction") @@ -96,7 +96,7 @@ func (sg *snapshotGenerator) performIssueWithProofs(transaction proto.Transactio } func (sg *snapshotGenerator) performReissueWithSig(transaction proto.Transaction, _ *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.ReissueWithSig) if !ok { return txSnapshot{}, errors.New("failed to convert interface to ReissueWithSig transaction") @@ -105,7 +105,7 @@ func (sg *snapshotGenerator) performReissueWithSig(transaction proto.Transaction } func (sg *snapshotGenerator) performReissueWithProofs(transaction proto.Transaction, - _ *performerInfo, _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *performerInfo, _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.ReissueWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to ReissueWithProofs transaction") @@ -114,7 +114,7 @@ func (sg *snapshotGenerator) performReissueWithProofs(transaction proto.Transact } func (sg *snapshotGenerator) performBurnWithSig(transaction proto.Transaction, _ *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.BurnWithSig) if !ok { return txSnapshot{}, errors.New("failed to convert interface to BurnWithSig transaction") @@ -123,7 +123,7 @@ func (sg *snapshotGenerator) performBurnWithSig(transaction proto.Transaction, _ } func (sg *snapshotGenerator) performBurnWithProofs(transaction proto.Transaction, _ *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.BurnWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to BurnWithProofs transaction") @@ -132,7 +132,7 @@ func (sg *snapshotGenerator) performBurnWithProofs(transaction proto.Transaction } func (sg *snapshotGenerator) performExchange(transaction proto.Transaction, _ *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(proto.Exchange) if !ok { return txSnapshot{}, errors.New("failed to convert interface to Exchange transaction") @@ -154,7 +154,7 @@ func (sg *snapshotGenerator) performExchange(transaction proto.Transaction, _ *p } func (sg *snapshotGenerator) performLeaseWithSig(transaction proto.Transaction, info *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.LeaseWithSig) if !ok { return txSnapshot{}, errors.New("failed to convert interface to LeaseWithSig transaction") @@ -163,7 +163,7 @@ func (sg *snapshotGenerator) performLeaseWithSig(transaction proto.Transaction, } func (sg *snapshotGenerator) performLeaseWithProofs(transaction proto.Transaction, info *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.LeaseWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to LeaseWithProofs transaction") @@ -172,7 +172,7 @@ func (sg *snapshotGenerator) performLeaseWithProofs(transaction proto.Transactio } func (sg *snapshotGenerator) performLeaseCancelWithSig(transaction proto.Transaction, info *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.LeaseCancelWithSig) if !ok { return txSnapshot{}, errors.New("failed to convert interface to LeaseCancelWithSig transaction") @@ -186,7 +186,7 @@ func (sg *snapshotGenerator) performLeaseCancelWithSig(transaction proto.Transac } func (sg *snapshotGenerator) performLeaseCancelWithProofs(transaction proto.Transaction, info *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.LeaseCancelWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to LeaseCancelWithProofs transaction") @@ -200,7 +200,7 @@ func (sg *snapshotGenerator) performLeaseCancelWithProofs(transaction proto.Tran } func (sg *snapshotGenerator) performCreateAliasWithSig(transaction proto.Transaction, _ *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.CreateAliasWithSig) if !ok { return txSnapshot{}, errors.New("failed to convert interface to CreateAliasWithSig transaction") @@ -209,7 +209,7 @@ func (sg *snapshotGenerator) performCreateAliasWithSig(transaction proto.Transac } func (sg *snapshotGenerator) performCreateAliasWithProofs(transaction proto.Transaction, _ *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.CreateAliasWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to CreateAliasWithProofs transaction") @@ -218,7 +218,7 @@ func (sg *snapshotGenerator) performCreateAliasWithProofs(transaction proto.Tran } func (sg *snapshotGenerator) performMassTransferWithProofs(transaction proto.Transaction, _ *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { _, ok := transaction.(*proto.MassTransferWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to CreateAliasWithProofs transaction") @@ -227,7 +227,7 @@ func (sg *snapshotGenerator) performMassTransferWithProofs(transaction proto.Tra } func (sg *snapshotGenerator) performDataWithProofs(transaction proto.Transaction, - _ *performerInfo, _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *performerInfo, _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.DataWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to DataWithProofs transaction") @@ -240,7 +240,7 @@ func (sg *snapshotGenerator) performDataWithProofs(transaction proto.Transaction } func (sg *snapshotGenerator) performSponsorshipWithProofs(transaction proto.Transaction, _ *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.SponsorshipWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to SponsorshipWithProofs transaction") @@ -249,7 +249,7 @@ func (sg *snapshotGenerator) performSponsorshipWithProofs(transaction proto.Tran } func (sg *snapshotGenerator) performSetScriptWithProofs(transaction proto.Transaction, info *performerInfo, - _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.SetScriptWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to SetScriptWithProofs transaction") @@ -268,7 +268,7 @@ func (sg *snapshotGenerator) performSetScriptWithProofs(transaction proto.Transa } func (sg *snapshotGenerator) performSetAssetScriptWithProofs(transaction proto.Transaction, - info *performerInfo, _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + info *performerInfo, _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.SetAssetScriptWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to SetAssetScriptWithProofs transaction") @@ -289,7 +289,7 @@ func (sg *snapshotGenerator) performSetAssetScriptWithProofs(transaction proto.T func (sg *snapshotGenerator) performInvokeScriptWithProofs(transaction proto.Transaction, info *performerInfo, _ *invocationResult, - balanceChanges txDiff) (txSnapshot, error) { + balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.InvokeScriptWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to InvokeScriptWithProofs transaction") @@ -300,7 +300,7 @@ func (sg *snapshotGenerator) performInvokeScriptWithProofs(transaction proto.Tra func (sg *snapshotGenerator) performInvokeExpressionWithProofs(transaction proto.Transaction, _ *performerInfo, _ *invocationResult, - balanceChanges txDiff) (txSnapshot, error) { + balanceChanges []balanceChanges) (txSnapshot, error) { if _, ok := transaction.(*proto.InvokeExpressionTransactionWithProofs); !ok { return txSnapshot{}, errors.New("failed to convert interface to InvokeExpressionWithProofs transaction") } @@ -308,7 +308,7 @@ func (sg *snapshotGenerator) performInvokeExpressionWithProofs(transaction proto } func (sg *snapshotGenerator) performEthereumTransactionWithProofs(transaction proto.Transaction, - _ *performerInfo, _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *performerInfo, _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { _, ok := transaction.(*proto.EthereumTransaction) if !ok { return txSnapshot{}, errors.New("failed to convert interface to EthereumTransaction transaction") @@ -317,7 +317,7 @@ func (sg *snapshotGenerator) performEthereumTransactionWithProofs(transaction pr } func (sg *snapshotGenerator) performUpdateAssetInfoWithProofs(transaction proto.Transaction, - _ *performerInfo, _ *invocationResult, balanceChanges txDiff) (txSnapshot, error) { + _ *performerInfo, _ *invocationResult, balanceChanges []balanceChanges) (txSnapshot, error) { tx, ok := transaction.(*proto.UpdateAssetInfoWithProofs) if !ok { return txSnapshot{}, errors.New("failed to convert interface to UpdateAssetInfoWithProofs transaction") @@ -338,15 +338,15 @@ type assetBalanceDiffKey struct { } type addressAssetBalanceDiff map[assetBalanceDiffKey]int64 -func (sg *snapshotGenerator) generateSnapshotForGenesisTx(balanceChanges txDiff) (txSnapshot, error) { +func (sg *snapshotGenerator) generateSnapshotForGenesisTx(balanceChanges []balanceChanges) (txSnapshot, error) { return sg.generateBalancesSnapshot(balanceChanges) } -func (sg *snapshotGenerator) generateSnapshotForPaymentTx(balanceChanges txDiff) (txSnapshot, error) { +func (sg *snapshotGenerator) generateSnapshotForPaymentTx(balanceChanges []balanceChanges) (txSnapshot, error) { return sg.generateBalancesSnapshot(balanceChanges) } -func (sg *snapshotGenerator) generateSnapshotForTransferTx(balanceChanges txDiff) (txSnapshot, error) { +func (sg *snapshotGenerator) generateSnapshotForTransferTx(balanceChanges []balanceChanges) (txSnapshot, error) { return sg.generateBalancesSnapshot(balanceChanges) } @@ -354,7 +354,7 @@ func (sg *snapshotGenerator) generateSnapshotForIssueTx( tx *proto.Issue, assetID crypto.Digest, info *performerInfo, - balanceChanges txDiff, + balanceChanges []balanceChanges, scriptEstimation *scriptEstimation, script proto.Script, ) (txSnapshot, error) { @@ -377,23 +377,14 @@ func (sg *snapshotGenerator) generateSnapshotForIssueTx( }, } - addrWavesBalanceDiff, addrAssetBalanceDiff, err := balanceDiffFromTxDiff(balanceChanges, sg.scheme) + addrWavesBalanceDiff, addrAssetBalanceDiff, err := sg.balanceDiffFromTxDiff(balanceChanges, sg.scheme) if err != nil { return txSnapshot{}, errors.Wrap(err, "failed to create balance diff from tx diff") } - // Remove the just issues snapshot from the diff, because it's not in the storage yet, - // so can't be processed with generateBalancesAtomicSnapshots. - var specialAssetSnapshot *proto.AssetBalanceSnapshot - for key, diffAmount := range addrAssetBalanceDiff { - if key.asset == proto.AssetIDFromDigest(assetID) { - // remove the element from the array - delete(addrAssetBalanceDiff, key) - specialAssetSnapshot = &proto.AssetBalanceSnapshot{ - Address: key.address, - AssetID: assetID, - Balance: uint64(diffAmount), - } - } + // Just issued Asset IDs are not in the storage yet, + // so can't be processed with generateBalancesAtomicSnapshots, unless we specify full asset ids from uncertain info + justIssuedAssets := justIssuedAssetsIDsToTails{ + proto.AssetIDFromDigest(assetID): proto.DigestTail(assetID), } var snapshot txSnapshot @@ -433,7 +424,7 @@ func (sg *snapshotGenerator) generateSnapshotForIssueTx( snapshot.regular = append(snapshot.regular, assetScriptSnapshot) } wavesBalancesSnapshot, assetBalancesSnapshot, leaseBalancesSnapshot, err := - sg.generateBalancesAtomicSnapshots(addrWavesBalanceDiff, addrAssetBalanceDiff) + sg.generateBalancesAtomicSnapshots(addrWavesBalanceDiff, addrAssetBalanceDiff, justIssuedAssets) if err != nil { return txSnapshot{}, errors.Wrap(err, "failed to build a snapshot from a genesis transaction") } @@ -446,9 +437,6 @@ func (sg *snapshotGenerator) generateSnapshotForIssueTx( for i := range assetBalancesSnapshot { snapshot.regular = append(snapshot.regular, &assetBalancesSnapshot[i]) } - if specialAssetSnapshot != nil { - snapshot.regular = append(snapshot.regular, specialAssetSnapshot) - } return snapshot, nil } @@ -457,7 +445,7 @@ func (sg *snapshotGenerator) generateSnapshotForReissueTx( assetID crypto.Digest, isReissuable bool, quantity uint64, - balanceChanges txDiff, + balanceChanges []balanceChanges, ) (txSnapshot, error) { // Modify asset. @@ -482,7 +470,7 @@ func (sg *snapshotGenerator) generateSnapshotForReissueTx( } func (sg *snapshotGenerator) generateSnapshotForBurnTx(assetID crypto.Digest, newQuantity uint64, - balanceChanges txDiff) (txSnapshot, error) { + balanceChanges []balanceChanges) (txSnapshot, error) { // Modify asset. quantityDiff := new(big.Int).SetUint64(newQuantity) @@ -507,7 +495,7 @@ func (sg *snapshotGenerator) generateSnapshotForBurnTx(assetID crypto.Digest, ne func (sg *snapshotGenerator) generateSnapshotForExchangeTx(sellOrder proto.Order, sellFee uint64, buyOrder proto.Order, buyFee uint64, volume uint64, - balanceChanges txDiff) (txSnapshot, error) { + balanceChanges []balanceChanges) (txSnapshot, error) { snapshot, err := sg.generateBalancesSnapshot(balanceChanges) if err != nil { return txSnapshot{}, errors.Wrap(err, "failed to generate a snapshot based on transaction's diffs") @@ -538,7 +526,7 @@ func (sg *snapshotGenerator) generateSnapshotForLeaseTx( tx *proto.Lease, txID *crypto.Digest, info *performerInfo, - balanceChanges txDiff, + balanceChanges []balanceChanges, ) (txSnapshot, error) { var recipientAddr proto.WavesAddress if addr := tx.Recipient.Address(); addr == nil { @@ -576,7 +564,7 @@ func (sg *snapshotGenerator) generateSnapshotForLeaseCancelTx( leaseID crypto.Digest, txID *crypto.Digest, cancelHeight proto.Height, - balanceChanges txDiff, + balanceChanges []balanceChanges, ) (txSnapshot, error) { var err error snapshot, err := sg.generateBalancesSnapshot(balanceChanges) @@ -600,7 +588,7 @@ func (sg *snapshotGenerator) generateSnapshotForCreateAliasTx( scheme proto.Scheme, senderPK crypto.PublicKey, alias proto.Alias, - balanceChanges txDiff, + balanceChanges []balanceChanges, ) (txSnapshot, error) { senderAddr, err := proto.NewAddressFromPublicKey(scheme, senderPK) if err != nil { @@ -618,12 +606,12 @@ func (sg *snapshotGenerator) generateSnapshotForCreateAliasTx( return snapshot, nil } -func (sg *snapshotGenerator) generateSnapshotForMassTransferTx(balanceChanges txDiff) (txSnapshot, error) { +func (sg *snapshotGenerator) generateSnapshotForMassTransferTx(balanceChanges []balanceChanges) (txSnapshot, error) { return sg.generateBalancesSnapshot(balanceChanges) } func (sg *snapshotGenerator) generateSnapshotForDataTx(senderAddress proto.WavesAddress, - entries []proto.DataEntry, balanceChanges txDiff) (txSnapshot, error) { + entries []proto.DataEntry, balanceChanges []balanceChanges) (txSnapshot, error) { snapshot, err := sg.generateBalancesSnapshot(balanceChanges) if err != nil { return txSnapshot{}, err @@ -637,7 +625,7 @@ func (sg *snapshotGenerator) generateSnapshotForDataTx(senderAddress proto.Waves } func (sg *snapshotGenerator) generateSnapshotForSponsorshipTx(assetID crypto.Digest, - minAssetFee uint64, balanceChanges txDiff) (txSnapshot, error) { + minAssetFee uint64, balanceChanges []balanceChanges) (txSnapshot, error) { snapshot, err := sg.generateBalancesSnapshot(balanceChanges) if err != nil { return txSnapshot{}, err @@ -651,7 +639,7 @@ func (sg *snapshotGenerator) generateSnapshotForSponsorshipTx(assetID crypto.Dig } func (sg *snapshotGenerator) generateSnapshotForSetScriptTx(senderPK crypto.PublicKey, script proto.Script, - scriptEstimation scriptEstimation, balanceChanges txDiff) (txSnapshot, error) { + scriptEstimation scriptEstimation, balanceChanges []balanceChanges) (txSnapshot, error) { snapshot, err := sg.generateBalancesSnapshot(balanceChanges) if err != nil { return txSnapshot{}, err @@ -680,7 +668,7 @@ func (sg *snapshotGenerator) generateSnapshotForSetScriptTx(senderPK crypto.Publ } func (sg *snapshotGenerator) generateSnapshotForSetAssetScriptTx(assetID crypto.Digest, script proto.Script, - balanceChanges txDiff, scriptEstimation scriptEstimation) (txSnapshot, error) { + balanceChanges []balanceChanges, scriptEstimation scriptEstimation) (txSnapshot, error) { snapshot, err := sg.generateBalancesSnapshot(balanceChanges) if err != nil { return txSnapshot{}, err @@ -849,7 +837,7 @@ func generateSnapshotsFromSponsoredAssetsUncertain( func (sg *snapshotGenerator) generateSnapshotForInvokeScript( scriptRecipient proto.Recipient, - balanceChanges txDiff, + balanceChanges []balanceChanges, scriptEstimation *scriptEstimation, ) (txSnapshot, error) { snapshot, err := sg.snapshotForInvoke(balanceChanges) @@ -875,30 +863,26 @@ func (sg *snapshotGenerator) generateSnapshotForInvokeScript( return snapshot, nil } -func (sg *snapshotGenerator) snapshotForInvoke(balanceChanges txDiff) (txSnapshot, error) { +type justIssuedAssetsIDsToTails map[proto.AssetID][proto.AssetIDTailSize]byte // asset id -> asset id tail + +func (sg *snapshotGenerator) snapshotForInvoke(balanceChanges []balanceChanges) (txSnapshot, error) { var snapshot txSnapshot - addrWavesBalanceDiff, addrAssetBalanceDiff, err := balanceDiffFromTxDiff(balanceChanges, sg.scheme) + addrWavesBalanceDiff, addrAssetBalanceDiff, err := sg.balanceDiffFromTxDiff(balanceChanges, sg.scheme) if err != nil { return txSnapshot{}, errors.Wrap(err, "failed to create balance diff from tx diff") } - // Remove the just issues snapshot from the diff, because it's not in the storage yet, - // so can't be processed with generateBalancesAtomicSnapshots. - var specialAssetsSnapshots []proto.AssetBalanceSnapshot - for key, diffAmount := range addrAssetBalanceDiff { + // Just issued Asset IDs are not in the storage yet, + // so can't be processed with generateBalancesAtomicSnapshots, unless we specify full asset ids from uncertain info + justIssuedAssets := justIssuedAssetsIDsToTails{} + for key := range addrAssetBalanceDiff { uncertainAssets := sg.stor.assets.uncertainAssetInfo - if _, ok := uncertainAssets[key.asset]; ok { - // remove the element from the map - delete(addrAssetBalanceDiff, key) - fullAssetID := proto.ReconstructDigest(key.asset, uncertainAssets[key.asset].assetInfo.tail) - specialAssetSnapshot := proto.AssetBalanceSnapshot{ - Address: key.address, - AssetID: fullAssetID, - Balance: uint64(diffAmount), + if uncertainInfo, ok := uncertainAssets[key.asset]; ok { + if !uncertainInfo.wasJustIssued { + continue } - specialAssetsSnapshots = append(specialAssetsSnapshots, specialAssetSnapshot) + justIssuedAssets[key.asset] = uncertainInfo.assetInfo.tail } } - assetsUncertain := sg.stor.assets.uncertainAssetInfo dataEntriesUncertain := sg.stor.accountsDataStor.uncertainEntries assetScriptsUncertain := sg.stor.scriptsStorage.uncertainAssetScriptsCopy() @@ -928,7 +912,7 @@ func (sg *snapshotGenerator) snapshotForInvoke(balanceChanges txDiff) (txSnapsho snapshot.regular = append(snapshot.regular, sponsoredAssetsSnapshots...) wavesBalancesSnapshot, assetBalancesSnapshot, leaseBalancesSnapshot, err := - sg.generateBalancesAtomicSnapshots(addrWavesBalanceDiff, addrAssetBalanceDiff) + sg.generateBalancesAtomicSnapshots(addrWavesBalanceDiff, addrAssetBalanceDiff, justIssuedAssets) if err != nil { return txSnapshot{}, errors.Wrap(err, "failed to build a snapshot from a genesis transaction") } @@ -941,18 +925,16 @@ func (sg *snapshotGenerator) snapshotForInvoke(balanceChanges txDiff) (txSnapsho for i := range assetBalancesSnapshot { snapshot.regular = append(snapshot.regular, &assetBalancesSnapshot[i]) } - for i := range specialAssetsSnapshots { - snapshot.regular = append(snapshot.regular, &specialAssetsSnapshots[i]) - } return snapshot, nil } -func (sg *snapshotGenerator) generateSnapshotForInvokeExpressionTx(balanceChanges txDiff) (txSnapshot, error) { +func (sg *snapshotGenerator) generateSnapshotForInvokeExpressionTx( + balanceChanges []balanceChanges) (txSnapshot, error) { return sg.snapshotForInvoke(balanceChanges) } func (sg *snapshotGenerator) generateSnapshotForEthereumInvokeScriptTx( - balanceChanges txDiff, + balanceChanges []balanceChanges, ) (txSnapshot, error) { return sg.snapshotForInvoke(balanceChanges) } @@ -961,7 +943,7 @@ func (sg *snapshotGenerator) generateSnapshotForUpdateAssetInfoTx( assetID crypto.Digest, assetName string, assetDescription string, - balanceChanges txDiff, + balanceChanges []balanceChanges, ) (txSnapshot, error) { snapshot, err := sg.generateBalancesSnapshot(balanceChanges) if err != nil { @@ -995,14 +977,37 @@ func (sg *snapshotGenerator) generateOrderAtomicSnapshot(orderID []byte, return orderSnapshot, nil } -func (sg *snapshotGenerator) generateBalancesSnapshot(balanceChanges txDiff) (txSnapshot, error) { +func (sg *snapshotGenerator) createInitialBlockSnapshot(minerAndRewardChanges []balanceChanges) (txSnapshot, error) { + addrWavesBalanceDiff, addrAssetBalanceDiff, err := sg.balanceDiffFromTxDiff(minerAndRewardChanges, sg.scheme) + if err != nil { + return txSnapshot{}, errors.Wrap(err, "failed to create balance diff from tx diff") + } + var snapshot txSnapshot + wavesBalancesSnapshot, assetBalancesSnapshot, leaseBalancesSnapshot, err := + sg.generateBalancesAtomicSnapshots(addrWavesBalanceDiff, addrAssetBalanceDiff, nil) + if err != nil { + return txSnapshot{}, errors.Wrap(err, "failed to build a snapshot from a genesis transaction") + } + for i := range wavesBalancesSnapshot { + snapshot.regular = append(snapshot.regular, &wavesBalancesSnapshot[i]) + } + for i := range leaseBalancesSnapshot { + snapshot.regular = append(snapshot.regular, &leaseBalancesSnapshot[i]) + } + for i := range assetBalancesSnapshot { + snapshot.regular = append(snapshot.regular, &assetBalancesSnapshot[i]) + } + return snapshot, nil +} + +func (sg *snapshotGenerator) generateBalancesSnapshot(balanceChanges []balanceChanges) (txSnapshot, error) { var snapshot txSnapshot - addrWavesBalanceDiff, addrAssetBalanceDiff, err := balanceDiffFromTxDiff(balanceChanges, sg.scheme) + addrWavesBalanceDiff, addrAssetBalanceDiff, err := sg.balanceDiffFromTxDiff(balanceChanges, sg.scheme) if err != nil { return txSnapshot{}, errors.Wrap(err, "failed to create balance diff from tx diff") } wavesBalancesSnapshot, assetBalancesSnapshot, leaseBalancesSnapshot, err := - sg.generateBalancesAtomicSnapshots(addrWavesBalanceDiff, addrAssetBalanceDiff) + sg.generateBalancesAtomicSnapshots(addrWavesBalanceDiff, addrAssetBalanceDiff, nil) if err != nil { return txSnapshot{}, errors.Wrap(err, "failed to build a snapshot from a genesis transaction") } @@ -1020,7 +1025,7 @@ func (sg *snapshotGenerator) generateBalancesSnapshot(balanceChanges txDiff) (tx func (sg *snapshotGenerator) generateBalancesAtomicSnapshots( addrWavesBalanceDiff addressWavesBalanceDiff, - addrAssetBalanceDiff addressAssetBalanceDiff) ( + addrAssetBalanceDiff addressAssetBalanceDiff, justIssuedAssets justIssuedAssetsIDsToTails) ( []proto.WavesBalanceSnapshot, []proto.AssetBalanceSnapshot, []proto.LeaseBalanceSnapshot, error) { @@ -1032,43 +1037,81 @@ func (sg *snapshotGenerator) generateBalancesAtomicSnapshots( return wavesBalanceSnapshot, nil, leaseBalanceSnapshot, nil } - assetBalanceSnapshot, err := sg.assetBalanceSnapshotFromBalanceDiff(addrAssetBalanceDiff) + assetBalanceSnapshot, err := sg.assetBalanceSnapshotFromBalanceDiff(addrAssetBalanceDiff, justIssuedAssets) if err != nil { return nil, nil, nil, errors.Wrap(err, "failed to construct asset balance snapshot") } return wavesBalanceSnapshot, assetBalanceSnapshot, leaseBalanceSnapshot, nil } -func balanceDiffFromTxDiff(diff txDiff, scheme proto.Scheme) (addressWavesBalanceDiff, addressAssetBalanceDiff, error) { - addrWavesBalanceDiff := make(addressWavesBalanceDiff) +func (sg *snapshotGenerator) addAssetBalanceDiffFromTxDiff(change balanceDiff, assetKey []byte, scheme proto.Scheme, + addrAssetBalanceDiff addressAssetBalanceDiff) error { + if change.balance == 0 { + return nil + } + assetBalanceK := &assetBalanceKey{} + if err := assetBalanceK.unmarshal(assetKey); err != nil { + return errors.Errorf("failed to unmarshal asset balance key: %v", err) + } + asset := assetBalanceK.asset + address, cnvrtErr := assetBalanceK.address.ToWavesAddress(scheme) + if cnvrtErr != nil { + return errors.Wrap(cnvrtErr, "failed to convert address id to waves address") + } + assetBalKey := assetBalanceDiffKey{address: address, asset: asset} + addrAssetBalanceDiff[assetBalKey] = change.balance + // for compatibility with the legacy state hashes + sg.stor.balances.addAssetBalanceChangeLegacySH(address, asset, change.balance) + return nil +} + +func (sg *snapshotGenerator) addWavesBalanceDiffFromTxDiff(change balanceDiff, wavesKey []byte, scheme proto.Scheme, + addrWavesBalanceDiff addressWavesBalanceDiff) error { + if change.balance == 0 && change.leaseOut == 0 && change.leaseIn == 0 { + return nil + } + wavesBalanceK := &wavesBalanceKey{} + if err := wavesBalanceK.unmarshal(wavesKey); err != nil { + return errors.Errorf("failed to unmarshal waves balance key: %v", err) + } + address, cnvrtErr := wavesBalanceK.address.ToWavesAddress(scheme) + if cnvrtErr != nil { + return errors.Wrap(cnvrtErr, "failed to convert address id to waves address") + } + addrWavesBalanceDiff[address] = change + // for compatibility with the legacy state hashes + sg.stor.balances.addWavesBalanceChangeLegacySH(address, change) + return nil +} + +func (sg *snapshotGenerator) balanceDiffFromTxDiff(balanceChanges []balanceChanges, + scheme proto.Scheme) (addressWavesBalanceDiff, addressAssetBalanceDiff, error) { addrAssetBalanceDiff := make(addressAssetBalanceDiff) - for balanceKeyString, diffAmount := range diff { - // construct address from key - wavesBalanceKey := &wavesBalanceKey{} - err := wavesBalanceKey.unmarshal([]byte(balanceKeyString)) - if err != nil { - // if the waves balance unmarshal failed, try to marshal into asset balance, and if it fails, then return the error - assetBalanceKey := &assetBalanceKey{} - mrshlErr := assetBalanceKey.unmarshal([]byte(balanceKeyString)) - if mrshlErr != nil { - return nil, nil, errors.Wrap(mrshlErr, "failed to convert balance key to asset balance key") + addrWavesBalanceDiff := make(addressWavesBalanceDiff) + for _, balanceChange := range balanceChanges { + if l := len(balanceChange.balanceDiffs); l != 1 { + return nil, nil, errors.Errorf( + "invalid balance diff count for the same address in the same block: want 1, got %d", l, + ) + } + switch len(balanceChange.key) { + case wavesBalanceKeySize: + err := sg.addWavesBalanceDiffFromTxDiff(balanceChange.balanceDiffs[0], balanceChange.key, + scheme, addrWavesBalanceDiff) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to add waves balance from tx diff") } - asset := assetBalanceKey.asset - address, cnvrtErr := assetBalanceKey.address.ToWavesAddress(scheme) - if cnvrtErr != nil { - return nil, nil, errors.Wrap(cnvrtErr, "failed to convert address id to waves address") + case assetBalanceKeySize: + err := sg.addAssetBalanceDiffFromTxDiff(balanceChange.balanceDiffs[0], balanceChange.key, + scheme, addrAssetBalanceDiff) + if err != nil { + return nil, nil, errors.Wrap(err, "failed to add asset balance from tx diff") } - assetBalKey := assetBalanceDiffKey{address: address, asset: asset} - addrAssetBalanceDiff[assetBalKey] = diffAmount.balance - continue - } - address, cnvrtErr := wavesBalanceKey.address.ToWavesAddress(scheme) - if cnvrtErr != nil { - return nil, nil, errors.Wrap(cnvrtErr, "failed to convert address id to waves address") + default: + return nil, nil, + errors.Errorf("wrong key size to calculate balance diff from tx diff, key size is %d", + len(balanceChange.key)) } - // if the waves balance diff is 0, it means it did not change. - // The reason for the 0 diff to exist is because of how LeaseIn and LeaseOut are handled in transaction differ. - addrWavesBalanceDiff[address] = diffAmount } return addrWavesBalanceDiff, addrAssetBalanceDiff, nil } @@ -1105,23 +1148,30 @@ func (sg *snapshotGenerator) wavesBalanceSnapshotFromBalanceDiff( } func (sg *snapshotGenerator) assetBalanceSnapshotFromBalanceDiff( - diff addressAssetBalanceDiff) ([]proto.AssetBalanceSnapshot, error) { - var assetBalances []proto.AssetBalanceSnapshot + diff addressAssetBalanceDiff, + justIssuedAssets justIssuedAssetsIDsToTails, +) ([]proto.AssetBalanceSnapshot, error) { + assetBalances := make([]proto.AssetBalanceSnapshot, 0, len(diff)) // add miner address to the diff for key, diffAmount := range diff { - balance, err := sg.stor.balances.newestAssetBalance(key.address.ID(), key.asset) - if err != nil { - return nil, errors.Wrap(err, "failed to receive sender's waves balance") + balance, balErr := sg.stor.balances.newestAssetBalance(key.address.ID(), key.asset) + if balErr != nil { + return nil, errors.Wrapf(balErr, "failed to receive sender's %q waves balance", key.address.String()) } - assetInfo, err := sg.stor.assets.newestAssetInfo(key.asset) - if err != nil { - return nil, errors.Wrap(err, "failed to get newest asset info") + assetIDTail, ok := justIssuedAssets[key.asset] + if !ok { // asset is not just issued, looking in the storage + constInfo, err := sg.stor.assets.newestConstInfo(key.asset) + if err != nil { + return nil, errors.Wrapf(err, "failed to get newest asset info for short assetID %q", + key.asset.String(), + ) + } + assetIDTail = constInfo.tail } - newBalance := proto.AssetBalanceSnapshot{ Address: key.address, - AssetID: key.asset.Digest(assetInfo.tail), + AssetID: key.asset.Digest(assetIDTail), Balance: uint64(int64(balance) + diffAmount), } assetBalances = append(assetBalances, newBalance) diff --git a/pkg/state/snapshot_generator_internal_test.go b/pkg/state/snapshot_generator_internal_test.go index e07df4ef9..b4c373e02 100644 --- a/pkg/state/snapshot_generator_internal_test.go +++ b/pkg/state/snapshot_generator_internal_test.go @@ -93,7 +93,7 @@ func TestDefaultTransferWavesAndAssetSnapshot(t *testing.T) { assert.NoError(t, err, "createDiffTransferWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} transactionSnapshot, err := to.tp.performTransferWithSig(tx, - defaultPerformerInfo(), nil, applicationRes.changes.diff) + defaultPerformerInfo(), nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform transfer tx") expectedSnapshot := txSnapshot{ regular: []proto.AtomicSnapshot{ @@ -137,7 +137,7 @@ func TestDefaultIssueTransactionSnapshot(t *testing.T) { assert.NoError(t, err, "createDiffIssueWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} transactionSnapshot, err := to.tp.performIssueWithSig(tx, - defaultPerformerInfo(), nil, applicationRes.changes.diff) + defaultPerformerInfo(), nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform issue tx") expectedSnapshot := txSnapshot{ @@ -204,7 +204,7 @@ func TestDefaultReissueSnapshot(t *testing.T) { assert.NoError(t, err, "createDiffReissueWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} transactionSnapshot, err := to.tp.performReissueWithSig(tx, - defaultPerformerInfo(), nil, applicationRes.changes.diff) + defaultPerformerInfo(), nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform reissue tx") expectedSnapshot := txSnapshot{ @@ -260,7 +260,7 @@ func TestDefaultBurnSnapshot(t *testing.T) { assert.NoError(t, err, "createDiffBurnWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} transactionSnapshot, err := to.tp.performBurnWithSig(tx, - defaultPerformerInfo(), nil, applicationRes.changes.diff) + defaultPerformerInfo(), nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform burn tx") expectedSnapshot := txSnapshot{ @@ -345,7 +345,7 @@ func TestDefaultExchangeTransaction(t *testing.T) { assert.NoError(t, err, "createDiffBurnWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} transactionSnapshot, err := to.tp.performExchange(tx, defaultPerformerInfo(), - nil, applicationRes.changes.diff) + nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform burn tx") expectedSnapshot := txSnapshot{ @@ -422,7 +422,7 @@ func TestDefaultLeaseSnapshot(t *testing.T) { assert.NoError(t, err, "createDiffBurnWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} pi := defaultPerformerInfo() - transactionSnapshot, err := to.tp.performLeaseWithSig(tx, pi, nil, applicationRes.changes.diff) + transactionSnapshot, err := to.tp.performLeaseWithSig(tx, pi, nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform burn tx") expectedSnapshot := txSnapshot{ @@ -499,7 +499,7 @@ func TestDefaultLeaseCancelSnapshot(t *testing.T) { assert.NoError(t, err, "createDiffBurnWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} pi := defaultPerformerInfo() - transactionSnapshot, err := to.tp.performLeaseCancelWithSig(tx, pi, nil, applicationRes.changes.diff) + transactionSnapshot, err := to.tp.performLeaseCancelWithSig(tx, pi, nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform burn tx") expectedSnapshot := txSnapshot{ @@ -556,7 +556,7 @@ func TestDefaultCreateAliasSnapshot(t *testing.T) { assert.NoError(t, err, "createDiffBurnWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} transactionSnapshot, err := to.tp.performCreateAliasWithSig(tx, defaultPerformerInfo(), - nil, applicationRes.changes.diff) + nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform burn tx") expectedSnapshot := txSnapshot{ @@ -606,7 +606,7 @@ func TestDefaultDataSnapshot(t *testing.T) { assert.NoError(t, err, "createDiffBurnWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} transactionSnapshot, err := to.tp.performDataWithProofs(tx, defaultPerformerInfo(), - nil, applicationRes.changes.diff) + nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform burn tx") expectedSnapshot := txSnapshot{ @@ -650,7 +650,7 @@ func TestDefaultSponsorshipSnapshot(t *testing.T) { assert.NoError(t, err, "createDiffBurnWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} transactionSnapshot, err := to.tp.performSponsorshipWithProofs(tx, - defaultPerformerInfo(), nil, applicationRes.changes.diff) + defaultPerformerInfo(), nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform burn tx") expectedSnapshot := txSnapshot{ @@ -729,7 +729,7 @@ func TestDefaultSetDappScriptSnapshot(t *testing.T) { assert.NoError(t, err, "createDiffBurnWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} transactionSnapshot, err := to.tp.performSetScriptWithProofs(tx, - defaultPerformerInfoWithChecker(checkerData), nil, applicationRes.changes.diff) + defaultPerformerInfoWithChecker(checkerData), nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform burn tx") expectedSnapshot := txSnapshot{ @@ -783,7 +783,7 @@ func TestDefaultSetScriptSnapshot(t *testing.T) { assert.NoError(t, err, "createDiffBurnWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} transactionSnapshot, err := to.tp.performSetScriptWithProofs(tx, - defaultPerformerInfoWithChecker(checkerData), nil, applicationRes.changes.diff) + defaultPerformerInfoWithChecker(checkerData), nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform burn tx") expectedSnapshot := txSnapshot{ @@ -836,7 +836,7 @@ func TestDefaultSetEmptyScriptSnapshot(t *testing.T) { assert.NoError(t, err, "createDiffBurnWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} transactionSnapshot, err := to.tp.performSetScriptWithProofs(tx, - defaultPerformerInfoWithChecker(checkerData), nil, applicationRes.changes.diff) + defaultPerformerInfoWithChecker(checkerData), nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform burn tx") expectedSnapshot := txSnapshot{ @@ -900,7 +900,7 @@ func TestDefaultSetAssetScriptSnapshot(t *testing.T) { assert.NoError(t, err, "createDiffBurnWithSig() failed") applicationRes := &applicationResult{changes: ch, checkerData: txCheckerData{}} transactionSnapshot, err := to.tp.performSetAssetScriptWithProofs(tx, - defaultPerformerInfoWithChecker(checkerData), nil, applicationRes.changes.diff) + defaultPerformerInfoWithChecker(checkerData), nil, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform burn tx") expectedSnapshot := txSnapshot{ @@ -980,7 +980,7 @@ func TestDefaultInvokeScriptSnapshot(t *testing.T) { transactionSnapshot, err := to.state.appender.txHandler.tp.performInvokeScriptWithProofs(tx, defaultPerformerInfoWithChecker(applicationRes.checkerData), - invocationRes, applicationRes.changes.diff) + invocationRes, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform invoke script tx") var dataEntrySnapshot *proto.DataEntriesSnapshot @@ -1118,7 +1118,7 @@ func TestNoExtraStaticAssetInfoSnapshot(t *testing.T) { transactionSnapshot, err := to.state.appender.txHandler.tp.performInvokeScriptWithProofs(tx, defaultPerformerInfoWithChecker(applicationRes.checkerData), - invocationRes, applicationRes.changes.diff) + invocationRes, applicationRes.changes.diff.balancesChanges()) assert.NoError(t, err, "failed to perform invoke script tx") expectedSnapshot := txSnapshot{ @@ -1221,7 +1221,7 @@ func TestLeaseAndLeaseCancelInTheSameInvokeTx(t *testing.T) { tx, defaultPerformerInfoWithChecker(applicationRes.checkerData), invocationRes, - applicationRes.changes.diff, + applicationRes.changes.diff.balancesChanges(), ) assert.NoError(t, err, "failed to perform invoke script tx") diff --git a/pkg/state/state.go b/pkg/state/state.go index 9a041d885..c3b99ad5b 100644 --- a/pkg/state/state.go +++ b/pkg/state/state.go @@ -603,9 +603,11 @@ func (s *stateManager) addGenesisBlock() error { return err } - if err := s.appender.applyAllDiffs(); err != nil { + err := s.appender.diffApplier.validateBalancesChanges(s.appender.diffStor.allChanges()) + if err != nil { return err } + if err := s.stor.prepareHashes(); err != nil { return err } @@ -1553,12 +1555,6 @@ func (s *stateManager) addBlocks() (*proto.Block, error) { return nil, wrapErr(ValidationError, verifyError) } - // Apply all the balance diffs accumulated from this blocks batch. - // This also validates diffs for negative balances. - if err := s.appender.applyAllDiffs(); err != nil { - return nil, err - } - // Retrieve and store legacy state hashes for each of new blocks. if shErr := s.stor.handleLegacyStateHashes(height, ids); shErr != nil { return nil, wrapErr(ModificationError, shErr) diff --git a/pkg/state/state_hasher.go b/pkg/state/state_hasher.go index 7f22727e6..c0c00d64b 100644 --- a/pkg/state/state_hasher.go +++ b/pkg/state/state_hasher.go @@ -28,39 +28,37 @@ func (s stateComponents) Less(i, j int) bool { } type stateForHashes struct { - pos map[string]int - data stateComponents + componentByKey map[string]stateComponent } func newStateForHashes() *stateForHashes { return &stateForHashes{ - pos: make(map[string]int), - data: make(stateComponents, 0), + componentByKey: make(map[string]stateComponent), } } func (s *stateForHashes) set(key string, c stateComponent) { - pos, ok := s.pos[key] - if !ok { - s.pos[key] = len(s.data) - s.data = append(s.data, c) - } else { - s.data[pos] = c - } + s.componentByKey[key] = c +} +func (s *stateForHashes) remove(key string) { + delete(s.componentByKey, key) } func (s *stateForHashes) reset() { - s.data = make(stateComponents, 0) - s.pos = make(map[string]int) + s.componentByKey = make(map[string]stateComponent) } func (s *stateForHashes) hash() (crypto.Digest, error) { - sort.Sort(s.data) + components := make(stateComponents, 0, len(s.componentByKey)) + for _, component := range s.componentByKey { + components = append(components, component) + } + sort.Sort(components) h, err := crypto.NewFastHash() if err != nil { return crypto.Digest{}, err } - for _, c := range s.data { + for _, c := range components { if err := c.writeTo(h); err != nil { return crypto.Digest{}, err } diff --git a/pkg/state/transaction_handler.go b/pkg/state/transaction_handler.go index 86fe225b9..7d9be0934 100644 --- a/pkg/state/transaction_handler.go +++ b/pkg/state/transaction_handler.go @@ -24,7 +24,7 @@ type scriptEstimation struct { func (e *scriptEstimation) isPresent() bool { return e != nil } type txCheckFunc func(proto.Transaction, *checkerInfo) (txCheckerData, error) -type txPerformFunc func(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) +type txPerformFunc func(proto.Transaction, *performerInfo, *invocationResult, []balanceChanges) (txSnapshot, error) type txCreateDiffFunc func(proto.Transaction, *differInfo) (txBalanceChanges, error) type txCountFeeFunc func(proto.Transaction, *feeDistribution) error @@ -215,7 +215,7 @@ func (h *transactionHandler) performTx( validatingUTX bool, invocationRes *invocationResult, applicationStatus bool, - balanceChanges txDiff, + balanceChanges []balanceChanges, ) (txSnapshot, error) { tv := tx.GetTypeInfo() funcs, ok := h.funcs[tv] @@ -239,13 +239,13 @@ func (h *transactionHandler) performTx( }, ) } else { - // TODO: generate balance atomic snapshots here for failed transactions - snapshot = txSnapshot{ - regular: []proto.AtomicSnapshot{ - &proto.TransactionStatusSnapshot{Status: proto.TransactionFailed}, - }, - internal: nil, + failedChangesSnapshots, err := h.tp.generateBalancesSnapshot(balanceChanges) + if err != nil { + return txSnapshot{}, errors.Wrap(err, "failed to create snapshots from failed changes") } + failedChangesSnapshots.regular = append(failedChangesSnapshots.regular, + &proto.TransactionStatusSnapshot{Status: proto.TransactionFailed}) + snapshot = failedChangesSnapshots } if err := snapshot.Apply(h.sa, tx, validatingUTX); err != nil { return txSnapshot{}, errors.Wrap(err, "failed to apply transaction snapshot") diff --git a/pkg/state/transaction_performer.go b/pkg/state/transaction_performer.go index facb8ab0e..da67b4358 100644 --- a/pkg/state/transaction_performer.go +++ b/pkg/state/transaction_performer.go @@ -5,32 +5,62 @@ import "github.com/wavesplatform/gowaves/pkg/proto" // transactionPerformer is a temporary interface for compatibility with legacy code. // It builds txSnapshot for each proto.Transaction type. type transactionPerformer interface { - performGenesis(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performPayment(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performTransferWithSig(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performTransferWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performIssueWithSig(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performIssueWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performReissueWithSig(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performReissueWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performBurnWithSig(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performBurnWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performExchange(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performLeaseWithSig(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performLeaseWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performLeaseCancelWithSig(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performLeaseCancelWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performCreateAliasWithSig(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performCreateAliasWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performMassTransferWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performDataWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performSponsorshipWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performSetScriptWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performSetAssetScriptWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performInvokeScriptWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performInvokeExpressionWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performEthereumTransactionWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) - performUpdateAssetInfoWithProofs(proto.Transaction, *performerInfo, *invocationResult, txDiff) (txSnapshot, error) + performGenesis(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performPayment(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performTransferWithSig(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performTransferWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performIssueWithSig(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performIssueWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performReissueWithSig(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performReissueWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performBurnWithSig(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performBurnWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performExchange(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performLeaseWithSig(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performLeaseWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performLeaseCancelWithSig(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performLeaseCancelWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performCreateAliasWithSig(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performCreateAliasWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performMassTransferWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performDataWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performSponsorshipWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performSetScriptWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performSetAssetScriptWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performInvokeScriptWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performInvokeExpressionWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performEthereumTransactionWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + performUpdateAssetInfoWithProofs(proto.Transaction, *performerInfo, + *invocationResult, []balanceChanges) (txSnapshot, error) + + createInitialBlockSnapshot(minerAndRewardChanges []balanceChanges) (txSnapshot, error) + // used for creating snapshots from failed changes + generateBalancesSnapshot(balanceChanges []balanceChanges) (txSnapshot, error) } type performerInfo struct { diff --git a/pkg/state/tx_snapshot.go b/pkg/state/tx_snapshot.go index 906c601e1..6203ca4dd 100644 --- a/pkg/state/tx_snapshot.go +++ b/pkg/state/tx_snapshot.go @@ -29,11 +29,9 @@ func (ts txSnapshot) Apply(a extendedSnapshotApplier, tx proto.Transaction, vali } // internal snapshots must be applied at the end for _, rs := range ts.regular { - if !rs.IsGeneratedByTxDiff() { - err := rs.Apply(a) - if err != nil { - return errors.Wrap(err, "failed to apply regular transaction snapshot") - } + err := rs.Apply(a) + if err != nil { + return errors.Wrap(err, "failed to apply regular transaction snapshot") } } for _, is := range ts.internal { @@ -47,3 +45,20 @@ func (ts txSnapshot) Apply(a extendedSnapshotApplier, tx proto.Transaction, vali } return nil } + +func (ts txSnapshot) ApplyInitialSnapshot(a extendedSnapshotApplier) error { + // internal snapshots must be applied at the end + for _, rs := range ts.regular { + err := rs.Apply(a) + if err != nil { + return errors.Wrap(err, "failed to apply regular transaction snapshot") + } + } + for _, is := range ts.internal { + err := is.ApplyInternal(a) + if err != nil { + return errors.Wrap(err, "failed to apply internal transaction snapshot") + } + } + return nil +}