Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions packages/api-derive/src/type/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,24 @@ import type { AccountId, Digest } from '@polkadot/types/interfaces';

export function extractAuthor (digest: Digest, sessionValidators: AccountId[]): AccountId | undefined {
const [citem] = digest.logs.filter((e) => e.isConsensus);
const [pitem] = digest.logs.filter((e) => e.isPreRuntime);
const preRuntimeItems = digest.logs.filter((e) => e.isPreRuntime);
const [sitem] = digest.logs.filter((e) => e.isSeal);
let accountId: AccountId | undefined;

try {
// This is critical to be first for BABE (before Consensus)
// If not first, we end up dropping the author at session-end
if (pitem) {
// Iterate through ALL preRuntime logs to find a recognized engine
// (parachains may have multiple preRuntime logs, e.g., CMLS + aura)
for (const pitem of preRuntimeItems) {
const [engine, data] = pitem.asPreRuntime;

accountId = engine.extractAuthor(data, sessionValidators);

// Stop at first recognized engine that returns an author
if (accountId) {
return accountId;
}
Comment on lines +23 to +25
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could be mistaken, but from a distance this fix seems a bit brittle. Are we certain that performing engine.extractAuthor on an entry that doesn't actually have an author will always return undefined? Aren't we chancing it a bit? Are we certain that the underlying encoded data can't yield a false-positive? What if one day the extractAuthor implementation changes?

Again, I'm not familiar with the implementation of extractAuthor. However, I think that it would be best to be more assertive and to first find out which one is the correct entry from where to extract the author, and only then get the author using the extractAuthor method.

It's just a thought. Please take it with a pinch of salt, because I really am not familiar with this logic in PJS. 🙂

Copy link
Contributor Author

@rajk93 rajk93 Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question! Your concern about relying on undefined returns is valid. However, the current approach is safe because extractAuthor() validates the engine identifier first, before interpreting any payload data:

public extractAuthor (bytes: Bytes, sessionValidators: AccountId[]): AccountId | undefined {
if (sessionValidators?.length) {
if (this.isAura) {
return getAuraAuthor(this.registry, bytes, sessionValidators);
} else if (this.isBabe) {
return getBabeAuthor(this.registry, bytes, sessionValidators);
}
}
// For pow & Nimbus, the bytes are the actual author
if (this.isPow || this.isNimbus) {
return getBytesAsAuthor(this.registry, bytes);
}
return undefined;
}

Example: When we call extractAuthor() on a CMLS preRuntime log:

  • Engine = 'CMLS' (4 bytes: 0x434d4c53)
  • Doesn't match 'aura', 'BABE', 'pow_' ...
  • Returns undefined without decoding the payload
  • Loop continues to the next preRuntime log

This means the engine ID acts as a gatekeeper -- random bytes in unrecognized engine payloads can't produce false positives because the engine string is checked before any data interpretation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the reasoning here. 👍

}

if (!accountId && citem) {
Expand Down