Skip to content

Commit

Permalink
add some design docs
Browse files Browse the repository at this point in the history
Signed-off-by: Neeharika-Sompalli <neeharika.sompalli@swirldslabs.com>
  • Loading branch information
Neeharika-Sompalli committed Jul 8, 2024
1 parent c85ef2c commit 5b3cdf2
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 77 deletions.
2 changes: 1 addition & 1 deletion hedera-node/docs/design/app/records.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ batching them together and sending records is handled by the `RecordStreamManage
The `RecordBuilderImpl` implements all known `RecordBuilder` subtypes. The implementation you see there is a stub,
but you can easily imagine the implementation.

**NEXT: [States](states.md)**
**NEXT: [SavePointStack](savepoint-stack.md)**
5 changes: 5 additions & 0 deletions hedera-node/docs/design/app/savepoint-stack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SavePointStack



**NEXT: [Signatures](signatures.md)**
2 changes: 2 additions & 0 deletions hedera-node/docs/design/app/signatures.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@ to be valid. It is possible that the signing requirements change between pre-han
accounts were modified between the time of pre-handle and handle.

![sig-expansion.drawio.png](images/Signature Expansion.drawio.png)

**NEXT: [States](states.md)**
Original file line number Diff line number Diff line change
Expand Up @@ -63,30 +63,61 @@ public interface SingleTransactionRecordBuilder {
*/
SingleTransactionRecordBuilder status(@NonNull ResponseCodeEnum status);

/**
* The transaction category of the transaction that created this record
* @return the transaction category
*/
HandleContext.TransactionCategory category();

/**
* The behavior of the record when the parent transaction fails
* @return the behavior
*/
ReversingBehavior reversingBehavior();

/**
* Removes all the side effects on the record when the parent transaction fails
*/
void nullOutSideEffectFields();

default boolean isPreceding() {
return category().equals(HandleContext.TransactionCategory.PRECEDING);
}

default boolean isInternalDispatch() {
return !category().equals(USER);
}

/**
* Sets the transactionID of the record based on the user transaction record.
* @return the builder
*/
SingleTransactionRecordBuilder syncBodyIdFromRecordId();

/**
* Sets the consensus timestamp of the record.
* @param now the consensus timestamp
* @return the builder
*/
SingleTransactionRecordBuilder consensusTimestamp(@NonNull final Instant now);

/**
* Returns the transaction ID of the record.
* @return the transaction ID
*/
TransactionID transactionID();

/**
* Sets the transaction ID of the record.
* @param transactionID the transaction ID
* @return the builder
*/
SingleTransactionRecordBuilder transactionID(@NonNull final TransactionID transactionID);

/**
* Sets the parent consensus timestamp of the record.
* @param parentConsensus the parent consensus timestamp
* @return the builder
*/
SingleTransactionRecordBuilder parentConsensus(@NonNull final Instant parentConsensus);

/**
* Returns whether the record is a base record builder. A base record builder is a record builder
* that is created when new stack is created.
* @return true if the record is a base record builder; otherwise false
*/
boolean isBaseRecordBuilder();

/**
Expand All @@ -105,6 +136,22 @@ static Transaction transactionWith(@NonNull TransactionBody body) {
.build();
}

/**
* Returns whether the transaction is a preceding transaction.
* @return {@code true} if the transaction is a preceding transaction; otherwise {@code false}
*/
default boolean isPreceding() {
return category().equals(HandleContext.TransactionCategory.PRECEDING);
}

/**
* Returns whether the transaction is an internal dispatch.
* @return true if the transaction is an internal dispatch; otherwise false
*/
default boolean isInternalDispatch() {
return !category().equals(USER);
}

/**
* Possible behavior of a SingleTransactionRecord when a parent transaction fails,
* and it is asked to be reverted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,75 +408,12 @@ private Stream<SingleTransactionRecord> recordStream(
@NonNull final UserTxn userTxn, @NonNull final List<SingleTransactionRecordBuilder> builders) {
final List<SingleTransactionRecord> records = new ArrayList<>();
TransactionID.Builder idBuilder = null;
int indexOfUserRecord = 0;
if (SIMULATE_MONO) {
for (int i = 0; i < builders.size(); i++) {
if (builders.get(i).category() == USER) {
indexOfUserRecord = i;
idBuilder = builders.get(i).transactionID().copyBuilder();
} else if (builders.get(i).isPreceding() && i > indexOfUserRecord) {
indexOfUserRecord++;
}
}
} else {
for (int i = 0; i < builders.size(); i++) {
if (builders.get(i).category() == USER) {
indexOfUserRecord = i;
idBuilder = builders.get(i).transactionID().copyBuilder();
break;
}
}
}
int numPrecedingSeen = 0;
int numFollowingSeen = 0;
Instant parentConsensus = null;
for (int i = 0; i < builders.size(); i++) {
final var builder = builders.get(i);
if (SIMULATE_MONO) {
if (builder.isPreceding()) {
final var nonce = totalPrecedingRecords - numPrecedingSeen;
builder.transactionID(requireNonNull(idBuilder).nonce(nonce).build())
.syncBodyIdFromRecordId()
.consensusTimestamp(userTxn.consensusNow().minusNanos(nonce));
records.add(0, ((SingleTransactionRecordBuilderImpl) builder).build());
numPrecedingSeen++;
} else if (builder.category() == USER) {
builder.consensusTimestamp(userTxn.consensusNow());
records.add(((SingleTransactionRecordBuilderImpl) builder).build());
} else {
final var nonce = totalPrecedingRecords + numFollowingSeen++ + 1;
builder.consensusTimestamp(userTxn.consensusNow().plusNanos(numFollowingSeen));
if (builder.transactionID() == null || TransactionID.DEFAULT.equals(builder.transactionID())) {
builder.transactionID(
requireNonNull(idBuilder).nonce(nonce).build())
.syncBodyIdFromRecordId();
}
if (builder.category() == CHILD) {
builder.parentConsensus(userTxn.consensusNow());
}
records.add(((SingleTransactionRecordBuilderImpl) builder).build());
}
} else {
final int nonce =
switch (builder.category()) {
case USER, SCHEDULED -> 0;
case PRECEDING, CHILD -> i < indexOfUserRecord ? indexOfUserRecord - i : i;
};
if (builder.transactionID() == null || TransactionID.DEFAULT.equals(builder.transactionID())) {
builder.transactionID(requireNonNull(idBuilder).nonce(nonce).build())
.syncBodyIdFromRecordId();
}
final var consensusNow = userTxn.consensusNow().plusNanos((long) i - indexOfUserRecord);
builder.consensusTimestamp(consensusNow);
if (builder.category() == CHILD) {
builder.parentConsensus(requireNonNull(parentConsensus));
}
if (builder.isBaseRecordBuilder()) {
parentConsensus = consensusNow;
}
records.add(((SingleTransactionRecordBuilderImpl) builder).build());
}
int indexOfUserRecord = getUserRecordIndex(builders);
if (indexOfUserRecord != -1) {
idBuilder = builders.get(indexOfUserRecord).transactionID().copyBuilder();
}

processBuilders(userTxn, builders, idBuilder, records, indexOfUserRecord);
recordCache.add(
userTxn.creatorInfo().nodeId(), requireNonNull(userTxn.txnInfo().payerID()), records);
return records.stream();
Expand Down Expand Up @@ -622,4 +559,102 @@ private UserTxn newUserTxn(
blockRecordManager,
this);
}

/* Helpers */

private void processBuilders(
final @NonNull UserTxn userTxn,
final @NonNull List<SingleTransactionRecordBuilder> builders,
final TransactionID.Builder idBuilder,
final List<SingleTransactionRecord> records,
final int indexOfUserRecord) {
int numPrecedingSeen = 0;
int numFollowingSeen = 0;
Instant parentConsensus = null;
for (int i = 0; i < builders.size(); i++) {
final var builder = builders.get(i);
if (SIMULATE_MONO) {
processSimulatingMono(userTxn, builder, records, idBuilder, numPrecedingSeen, numFollowingSeen);
if (builder.isPreceding()) {
numPrecedingSeen++;
} else {
numFollowingSeen++;
}
} else {
final int nonce = getNonce(indexOfUserRecord, builder, i);
if (builder.transactionID() == null || TransactionID.DEFAULT.equals(builder.transactionID())) {
builder.transactionID(requireNonNull(idBuilder).nonce(nonce).build())
.syncBodyIdFromRecordId();
}
final var consensusNow = userTxn.consensusNow().plusNanos((long) i - indexOfUserRecord);
builder.consensusTimestamp(consensusNow);
if (builder.category() == CHILD) {
builder.parentConsensus(requireNonNull(parentConsensus));
}
if (builder.isBaseRecordBuilder()) {
parentConsensus = consensusNow;
}
records.add(((SingleTransactionRecordBuilderImpl) builder).build());
}
}
}

private void processSimulatingMono(
UserTxn userTxn,
SingleTransactionRecordBuilder builder,
List<SingleTransactionRecord> records,
TransactionID.Builder idBuilder,
int numPrecedingSeen,
int numFollowingSeen) {
if (builder.isPreceding()) {
final var nonce = totalPrecedingRecords - numPrecedingSeen;
builder.transactionID(requireNonNull(idBuilder).nonce(nonce).build())
.syncBodyIdFromRecordId()
.consensusTimestamp(userTxn.consensusNow().minusNanos(nonce));
records.add(0, ((SingleTransactionRecordBuilderImpl) builder).build());
} else if (builder.category() == USER) {
builder.consensusTimestamp(userTxn.consensusNow());
records.add(((SingleTransactionRecordBuilderImpl) builder).build());
} else {
final var nonce = totalPrecedingRecords + numFollowingSeen + 1;
builder.consensusTimestamp(userTxn.consensusNow().plusNanos(numFollowingSeen + 1));
if (builder.transactionID() == null || TransactionID.DEFAULT.equals(builder.transactionID())) {
builder.transactionID(requireNonNull(idBuilder).nonce(nonce).build())
.syncBodyIdFromRecordId();
}
if (builder.category() == CHILD) {
builder.parentConsensus(userTxn.consensusNow());
}
records.add(((SingleTransactionRecordBuilderImpl) builder).build());
}
}

private static int getNonce(
final int indexOfUserRecord, final SingleTransactionRecordBuilder builder, final int i) {
return switch (builder.category()) {
case USER, SCHEDULED -> 0;
case PRECEDING, CHILD -> i < indexOfUserRecord ? indexOfUserRecord - i : i;
};
}

private int getUserRecordIndex(final List<SingleTransactionRecordBuilder> builders) {
int indexOfUserRecord = -1;
if (SIMULATE_MONO) {
for (int i = 0; i < builders.size(); i++) {
if (builders.get(i).category() == USER) {
indexOfUserRecord = i;
} else if (builders.get(i).isPreceding() && i > indexOfUserRecord) {
indexOfUserRecord++;
}
}
} else {
for (int i = 0; i < builders.size(); i++) {
if (builders.get(i).category() == USER) {
indexOfUserRecord = i;
break;
}
}
}
return indexOfUserRecord;
}
}

0 comments on commit 5b3cdf2

Please sign in to comment.