|
15 | 15 | */ |
16 | 16 |
|
17 | 17 | import { sha3_256 } from 'js-sha3'; |
| 18 | +import { UnresolvedMapping } from '../../core/utils/UnresolvedMapping'; |
18 | 19 | import { GeneratorUtils } from '../../infrastructure/catbuffer/GeneratorUtils'; |
19 | 20 | import { Address } from '../account/Address'; |
| 21 | +import { NetworkType } from '../blockchain/NetworkType'; |
20 | 22 | import { MosaicId } from '../mosaic/MosaicId'; |
21 | 23 | import { NamespaceId } from '../namespace/NamespaceId'; |
22 | 24 | import { UInt64 } from '../UInt64'; |
23 | 25 | import { ReceiptType } from './ReceiptType'; |
24 | 26 | import { ReceiptVersion } from './ReceiptVersion'; |
25 | 27 | import { ResolutionEntry } from './ResolutionEntry'; |
26 | 28 | import { ResolutionType } from './ResolutionType'; |
27 | | -import { NetworkType } from "../blockchain/NetworkType"; |
28 | | -import { UnresolvedMapping } from "../../core/utils/UnresolvedMapping"; |
29 | 29 |
|
30 | 30 | /** |
31 | 31 | * When a transaction includes an alias, a so called resolution statement reflects the resolved value for that block: |
@@ -84,6 +84,118 @@ export class ResolutionStatement { |
84 | 84 | return hasher.hex().toUpperCase(); |
85 | 85 | } |
86 | 86 |
|
| 87 | + /** |
| 88 | + * @internal |
| 89 | + * Find resolution entry for given primaryId and secondaryId |
| 90 | + * @param primaryId Primary id |
| 91 | + * @param secondaryId Secondary id |
| 92 | + * @returns {ResolutionEntry | undefined} |
| 93 | + */ |
| 94 | + public getResolutionEntryById(primaryId: number, secondaryId: number): ResolutionEntry | undefined { |
| 95 | + /* |
| 96 | + Primary id and secondary id do not specifically map to the exact transaction index on the same block. |
| 97 | + The ids are just the order of the resolution reflecting on the order of transactions (ordered by index). |
| 98 | + E.g 1 - Bob -> 1 random.token -> Alice |
| 99 | + 2 - Carol -> 1 random.token > Denis |
| 100 | + Based on above example, 2 transactions (index 0 & 1) are created on the same block, however, only 1 |
| 101 | + resolution entry get generated for both. |
| 102 | + */ |
| 103 | + const resolvedPrimaryId = this.getMaxAvailablePrimaryId(primaryId); |
| 104 | + |
| 105 | + /* |
| 106 | + If no primaryId found, it means there's no resolution entry available for the process. Invalid entry. |
| 107 | +
|
| 108 | + e.g. Given: |
| 109 | + Entries: [{P:2, S:0}, {P:5, S:6}] |
| 110 | + Transaction: [Inx:1(0+1), AggInx:0] |
| 111 | + It should return Entry: undefined |
| 112 | + */ |
| 113 | + if (resolvedPrimaryId === 0) { |
| 114 | + return undefined; |
| 115 | + } else if (primaryId > resolvedPrimaryId) { |
| 116 | + /* |
| 117 | + If the transaction index is greater than the overall most recent source primary id. |
| 118 | + Use the most recent resolution entry (Max.PrimaryId + Max.SecondaryId) |
| 119 | +
|
| 120 | + e.g. Given: |
| 121 | + Entries: [{P:1, S:0}, {P:2, S:0}, {P:4, S:2}, {P:4, S:4} {P:7, S:6}] |
| 122 | + Transaction: [Inx:5(4+1), AggInx:0] |
| 123 | + It should return Entry: {P:4, S:4} |
| 124 | +
|
| 125 | + e.g. Given: |
| 126 | + Entries: [{P:1, S:0}, {P:2, S:0}, {P:4, S:2}, {P:4, S:4}, {P:7, S:6}] |
| 127 | + Transaction: [Inx:3(2+1), AggInx:0] |
| 128 | + It should return Entry: {P:2, S:0} |
| 129 | + */ |
| 130 | + return this.resolutionEntries |
| 131 | + .find((entry) => entry.source.primaryId === resolvedPrimaryId && |
| 132 | + entry.source.secondaryId === this.getMaxSecondaryIdByPrimaryId(resolvedPrimaryId)); |
| 133 | + } |
| 134 | + |
| 135 | + // When transaction index matches a primaryId, get the most recent secondaryId (resolvedPrimaryId can only <= primaryId) |
| 136 | + const resolvedSecondaryId = this.getMaxSecondaryIdByPrimaryIdAndSecondaryId(resolvedPrimaryId, secondaryId); |
| 137 | + |
| 138 | + /* |
| 139 | + If no most recent secondaryId matched transaction index, find previous resolution entry (most recent). |
| 140 | + This means the resolution entry for the specific inner transaction (inside Aggregate) / |
| 141 | + was generated previously outside the aggregate. It should return the previous entry (previous primaryId) |
| 142 | +
|
| 143 | + e.g. Given: |
| 144 | + Entries: [{P:1, S:0}, {P:2, S:0}, {P:5, S:6}] |
| 145 | + Transaction: [Inx:5(4+1), AggInx:3(2+1)] |
| 146 | + It should return Entry: {P:2, S:0} |
| 147 | + */ |
| 148 | + if (resolvedSecondaryId === 0 && resolvedSecondaryId !== secondaryId) { |
| 149 | + const lastPrimaryId = this.getMaxAvailablePrimaryId(resolvedPrimaryId - 1); |
| 150 | + return this.resolutionEntries.find((entry) => entry.source.primaryId === lastPrimaryId && |
| 151 | + entry.source.secondaryId === this.getMaxSecondaryIdByPrimaryId(lastPrimaryId)); |
| 152 | + } |
| 153 | + |
| 154 | + /* |
| 155 | + Found a matched resolution entry on both primaryId and secondaryId |
| 156 | +
|
| 157 | + e.g. Given: |
| 158 | + Entries: [{P:1, S:0}, {P:2, S:0}, {P:5, S:6}] |
| 159 | + Transaction: [Inx:5(4+1), AggInx:6(2+1)] |
| 160 | + It should return Entry: {P:5, S:6} |
| 161 | + */ |
| 162 | + return this.resolutionEntries |
| 163 | + .find((entry) => entry.source.primaryId === resolvedPrimaryId && entry.source.secondaryId === resolvedSecondaryId); |
| 164 | + } |
| 165 | + |
| 166 | + /** |
| 167 | + * @internal |
| 168 | + * Get max secondary id by a given primaryId |
| 169 | + * @param primaryId Primary source id |
| 170 | + * @returns {number} |
| 171 | + */ |
| 172 | + private getMaxSecondaryIdByPrimaryId(primaryId: number): number { |
| 173 | + return Math.max(...this.resolutionEntries.filter((entry) => entry.source.primaryId === primaryId) |
| 174 | + .map((filtered) => filtered.source.secondaryId)); |
| 175 | + } |
| 176 | + |
| 177 | + /** |
| 178 | + * Get most `recent` available secondary id by a given primaryId |
| 179 | + * @param primaryId Primary source id |
| 180 | + * @param secondaryId Secondary source id |
| 181 | + * @returns {number} |
| 182 | + */ |
| 183 | + private getMaxSecondaryIdByPrimaryIdAndSecondaryId(primaryId: number, secondaryId: number): number { |
| 184 | + return Math.max(...this.resolutionEntries.filter((entry) => entry.source.primaryId === primaryId) |
| 185 | + .map((filtered) => secondaryId >= filtered.source.secondaryId ? filtered.source.secondaryId : 0)); |
| 186 | + } |
| 187 | + |
| 188 | + /** |
| 189 | + * @internal |
| 190 | + * Get most `recent` primary source id by a given id (transaction index) as PrimaryId might not be the same as block transaction index. |
| 191 | + * @param primaryId Primary source id |
| 192 | + * @returns {number} |
| 193 | + */ |
| 194 | + private getMaxAvailablePrimaryId(primaryId: number): number { |
| 195 | + return Math.max(...this.resolutionEntries |
| 196 | + .map((entry) => primaryId >= entry.source.primaryId ? entry.source.primaryId : 0)); |
| 197 | + } |
| 198 | + |
87 | 199 | /** |
88 | 200 | * @internal |
89 | 201 | * Generate buffer for unresulved |
|
0 commit comments