Skip to content
Open
Show file tree
Hide file tree
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
443 changes: 443 additions & 0 deletions adr/0007-linear-leios-musashi-network-mini-protocols.md

Large diffs are not rendered by default.

443 changes: 443 additions & 0 deletions adr/0008-leios-musashi-code-review-findings-fable.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class Constants {
public static final long PREPROD_PROTOCOL_MAGIC = NetworkType.PREPROD.getProtocolMagic();
public static final long PREVIEW_PROTOCOL_MAGIC = NetworkType.PREVIEW.getProtocolMagic();
public static final long SANCHONET_PROTOCOL_MAGIC = NetworkType.SANCHONET.getProtocolMagic();
public static final long MUSASHI_PROTOCOL_MAGIC = 164L;

/**
* @deprecated Use MAINNET_PUBLIC_RELAY_ADDR
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.bloxbean.cardano.yaci.core.protocol.leios;

import java.util.Arrays;
import java.util.Objects;

public final class LeiosPoint {

Check warning on line 6 in core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/leios/LeiosPoint.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this class declaration to use 'record LeiosPoint(long slot, byte[] ebHash)'.

See more on https://sonarcloud.io/project/issues?id=bloxbean_yaci&issues=AZ8jBm9Tz13szBXuRsCc&open=AZ8jBm9Tz13szBXuRsCc&pullRequest=167
public static final int EB_HASH_LENGTH = 32;

private final long slot;
private final byte[] ebHash;

public LeiosPoint(long slot, byte[] ebHash) {
if (slot < 0) {
throw new IllegalArgumentException("slot must be non-negative");
}
Objects.requireNonNull(ebHash, "ebHash");
if (ebHash.length != EB_HASH_LENGTH) {
throw new IllegalArgumentException("ebHash must be " + EB_HASH_LENGTH + " bytes");
}

this.slot = slot;
this.ebHash = Arrays.copyOf(ebHash, ebHash.length);
}

public long getSlot() {
return slot;
}

public byte[] getEbHash() {
return Arrays.copyOf(ebHash, ebHash.length);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof LeiosPoint that)) {
return false;
}
return slot == that.slot && Arrays.equals(ebHash, that.ebHash);
}

@Override
public int hashCode() {
int result = Long.hashCode(slot);
result = 31 * result + Arrays.hashCode(ebHash);
return result;
}

@Override
public String toString() {
return "LeiosPoint{" +
"slot=" + slot +
", ebHash=" + Arrays.toString(ebHash) +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.bloxbean.cardano.yaci.core.protocol.leios;

public final class LeiosProtocolConstants {
public static final int LEIOS_NOTIFY_PROTOCOL_ID = 18;
public static final int LEIOS_FETCH_PROTOCOL_ID = 19;

private LeiosProtocolConstants() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.bloxbean.cardano.yaci.core.protocol.leios;

import com.bloxbean.cardano.yaci.core.util.CborSerializationUtil;
import com.bloxbean.cardano.yaci.core.util.HexUtil;

import java.util.Arrays;
import java.util.Objects;

public final class LeiosRawCbor {

Check warning on line 9 in core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/leios/LeiosRawCbor.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this class declaration to use 'record LeiosRawCbor(byte[] cbor)'.

See more on https://sonarcloud.io/project/issues?id=bloxbean_yaci&issues=AZ8jBm9bz13szBXuRsCd&open=AZ8jBm9bz13szBXuRsCd&pullRequest=167
private final byte[] cbor;

public LeiosRawCbor(byte[] cbor) {
Objects.requireNonNull(cbor, "cbor");
if (CborSerializationUtil.deserialize(cbor).length != 1) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The "opaque bytes" wrapper eagerly full-decodes every payload it wraps.

CborSerializationUtil.deserialize(cbor) here fully parses the payload purely to assert there is exactly one top-level item — on every inbound endorser block, announcement, vote and tx-list.

That contradicts the stated design ("payloads are carried as opaque CBOR, block-level deserialization deferred"): we pay a full decode of a multi-MB EB body to check a cardinality invariant, and malformed or oversized inbound CBOR aborts message construction inside what is supposed to be a dumb byte holder.

A boundary scan (single-item / end-offset check) is enough for the invariant and is O(structure) without materializing the tree — CborByteScanner in #168 does exactly this. Alternatively, make the check opt-in for the of(...) factory used on outbound data only.

throw new IllegalArgumentException("raw CBOR must contain exactly one top-level value");
}
this.cbor = Arrays.copyOf(cbor, cbor.length);
}

public static LeiosRawCbor of(byte[] cbor) {
return new LeiosRawCbor(cbor);
}

public byte[] getCbor() {
return Arrays.copyOf(cbor, cbor.length);
}

public String toHex() {
return HexUtil.encodeHexString(cbor);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof LeiosRawCbor that)) {
return false;
}
return Arrays.equals(cbor, that.cbor);
}

@Override
public int hashCode() {
return Arrays.hashCode(cbor);
}

@Override
public String toString() {
return "LeiosRawCbor{bytes=" + cbor.length + '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package com.bloxbean.cardano.yaci.core.protocol.leios;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;

public final class LeiosTxBitmap {

Check warning on line 10 in core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/leios/LeiosTxBitmap.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this class declaration to use 'record LeiosTxBitmap(Map<...> windows)'.

See more on https://sonarcloud.io/project/issues?id=bloxbean_yaci&issues=AZ8jBm4lz13szBXuRsCb&open=AZ8jBm4lz13szBXuRsCb&pullRequest=167
public static final int TXS_PER_WINDOW = 64;
public static final int MAX_WINDOW_INDEX = 0xFFFF;

private final Map<Integer, Long> windows;

public LeiosTxBitmap(Map<Integer, Long> windows) {
Objects.requireNonNull(windows, "windows");

TreeMap<Integer, Long> sorted = new TreeMap<>();
windows.forEach((window, mask) -> {
validateWindow(window);
if (mask != 0L) {
sorted.put(window, mask);
}
});

this.windows = Collections.unmodifiableMap(new LinkedHashMap<>(sorted));
}

public static LeiosTxBitmap empty() {
return new LeiosTxBitmap(Map.of());
}

public static LeiosTxBitmap firstN(int count) {
if (count < 0) {
throw new IllegalArgumentException("count must be non-negative");
}
if (count == 0) {
return empty();
}

Map<Integer, Long> windows = new LinkedHashMap<>();
int remaining = count;
int window = 0;
while (remaining > 0) {
validateWindow(window);
int inWindow = Math.min(TXS_PER_WINDOW, remaining);
windows.put(window, firstBits(inWindow));
remaining -= inWindow;
window++;
}

return new LeiosTxBitmap(windows);
}

public static LeiosTxBitmap fromIndices(int... indices) {
Objects.requireNonNull(indices, "indices");
Map<Integer, Long> windows = new LinkedHashMap<>();
for (int index : indices) {
addIndex(windows, index);
}
return new LeiosTxBitmap(windows);
}

public static LeiosTxBitmap fromIndices(Collection<Integer> indices) {
Objects.requireNonNull(indices, "indices");
Map<Integer, Long> windows = new LinkedHashMap<>();
for (Integer index : indices) {
if (index == null) {
throw new IllegalArgumentException("index cannot be null");
}
addIndex(windows, index);
}
return new LeiosTxBitmap(windows);
}

public Map<Integer, Long> getWindows() {
return windows;
}

public boolean isEmpty() {
return windows.isEmpty();
}

public Long getMask(int window) {
validateWindow(window);
return windows.get(window);
}

private static void addIndex(Map<Integer, Long> windows, int index) {
if (index < 0) {
throw new IllegalArgumentException("transaction index must be non-negative");
}
int window = index / TXS_PER_WINDOW;
validateWindow(window);

int offset = index % TXS_PER_WINDOW;
long bit = 1L << (TXS_PER_WINDOW - 1 - offset);
windows.merge(window, bit, (left, right) -> left | right);
}

private static long firstBits(int count) {
if (count < 0 || count > TXS_PER_WINDOW) {
throw new IllegalArgumentException("count must be between 0 and " + TXS_PER_WINDOW);
}
if (count == 0) {
return 0L;
}
if (count == TXS_PER_WINDOW) {
return -1L;
}
return -1L << (TXS_PER_WINDOW - count);
}

private static void validateWindow(int window) {
if (window < 0 || window > MAX_WINDOW_INDEX) {
throw new IllegalArgumentException("window must be between 0 and " + MAX_WINDOW_INDEX);
}
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof LeiosTxBitmap that)) {
return false;
}
return windows.equals(that.windows);
}

@Override
public int hashCode() {
return windows.hashCode();
}

@Override
public String toString() {
return "LeiosTxBitmap{" +
"windows=" + windows +
'}';
}
}
Loading
Loading