package bftsmart.demo.itotcca;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import bftsmart.demo.itotcca.IdManager.IdType;
import io.etcd.jetcd.ByteSequence;
import io.etcd.jetcd.Client;
import io.etcd.jetcd.kv.GetResponse;
public class IdGenerationCall {
// Redis and etcd clients (assume these are initialized elsewhere in the application)
private static final IdType FULL_CLASS_UR_KEY = null;
private static final IdType FULL_CLASS_U_KEY = null;
private static MemberOrClassId memberOrClassId;
private static GeneralMessage request;
private static final IdType FULL_CLASS_A_KEY = null;
private static String recordOid;
// Store the mapping in both Redis and Etcd
public static void storeMapping(String key, String value) {
try {
// Store in Etcd
etcdClient.getKVClient().put(
io.etcd.jetcd.ByteSequence.from(key.getBytes()),
io.etcd.jetcd.ByteSequence.from(value.getBytes())
);
} catch (Exception e) {
throw new RuntimeException("Error storing mapping: " + e.getMessage(), e);
}
}
// Retrieve the mapping from Redis or Etcd
private static Cluster networkId;
private static final String MAPPING_PREFIX = "idMappings/";
private static Client etcdClient = initializeEtcdClient();
private static String databaseOid;
private static String schemaOid;
private static String tableOid;
private static IdType idType;
private static MemberOrClassId identifier;
private static String supernodeIdStr;
private static URI[] initializeEtcdEndpoints() {
String endpoints = System.getenv("ETCD_ENDPOINTS");
return Arrays.stream(endpoints.split(","))
.map(String::trim)
.map(URI::create)
.toArray(URI[]::new);
}
// Method to initialize the Etcd client
public static Client initializeEtcdClient() {
URI[] etcdEndpoints = initializeEtcdEndpoints();
try {
// Build and return the Etcd client
return Client.builder()
.endpoints(etcdEndpoints)
.build();
} catch (Exception e) {
throw new IllegalStateException("Failed to initialize Etcd client: " + e.getMessage(), e);
}
}
public static String getMapping(String key) {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("Key cannot be null or empty");
}
try {
ByteSequence keyBS = ByteSequence.from(MAPPING_PREFIX + key, StandardCharsets.UTF_8);
CompletableFuture getFuture = etcdClient.getKVClient().get(keyBS);
GetResponse response = getFuture.get(); // Blocking call to fetch value
if (!response.getKvs().isEmpty()) {
return response.getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
}
return null; // Key not found
} catch (Exception e) {
System.err.println("Error accessing Etcd: " + e.getMessage());
e.printStackTrace();
return null;
}
}
// Generate or retrieve an ID for an entire Class_A Object
public static String generateFullClassAKey(IdType FULL_CLASS_A_KEY, Cluster networkId, MemberOrClassId memberOrClassId, GeneralMessage request) throws Exception {
// Key includes IdType to enforce independence or consistency as needed
String key = "FULL_CLASS_A_KEY-" + networkId + "-" + memberOrClassId + "-" + Long.toHexString(IdManager.oidMap.get("databaseOid")) + "-" + Long.toHexString(IdManager.oidMap.get("tableOid")) + "-" + Long.toHexString(IdManager.oidMap.get("tableOid")) + "-" + recordOid;
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
return existingId;
}
// Generate and store a new ID
String newId = IdManager.createNewFullAId(IdType.FULL_CLASS_A_KEY, networkId, memberOrClassId, request);
storeMapping(key, newId);
return newId;
}
// Generate or retrieve an ID for an entire Class_UR Object
public static String generateFullBFTClassURKey(Cluster networkId, MemberOrClassId memberOrClassId2, String databaseOid, String schemaOid, String tableOid, String recordOid) throws Exception {
// Key includes IdType to enforce independence or consistency as needed
String key = "FULL_BFT_CLASS_UR_KEY-" + networkId + "-" + memberOrClassId2 + "-" + Long.toHexString(IdManager.oidMap.get("databaseOid")) + "-" + Long.toHexString(IdManager.oidMap.get("tableOid")) + "-" + Long.toHexString(IdManager.oidMap.get("tableOid")) + "-" + recordOid;
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
return existingId;
}
// Generate and store a new ID
String newId = IdManager.createNewFullURId(IdType.FULL_BFT_CLASS_UR_KEY, networkId, memberOrClassId2, request);
storeMapping(key, newId);
return newId;
}
// Generate or retrieve an ID for an entire Class_U Object
public static String generateFullBFTClassUKey(Cluster networkId, MemberOrClassId memberOrClassId, String databaseOid, String schemaOid, String tableOid) throws Exception {
// Key includes IdType to enforce independence or consistency as needed
String key = "FULL_BFT_CLASS_U_KEY-" + networkId + "-" + memberOrClassId + "-" + Long.toHexString(IdManager.oidMap.get("databaseOid")) + "-" + Long.toHexString(2200L) + "-" + Long.toHexString(IdManager.oidMap.get("schemaOid")) + "-" + Long.toHexString(IdManager.oidMap.get("tableOid"));
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
return existingId;
}
// Generate and store a new ID
String newId = IdManager.createAndStoreFullUId(IdType.FULL_BFT_CLASS_U_KEY, networkId, memberOrClassId, request);
storeMapping(key, newId);
return newId;
}
// Generate or retrieve an ID for an entire Class_UR Object
public static String generateFullDHTClassURKey(Cluster networkId, MemberOrClassId memberOrClassId2, String databaseOid, String schemaOid, String tableOid, String recordOid) throws Exception {
// Key includes IdType to enforce independence or consistency as needed
String key = "FULL_DHT_CLASS_UR_KEY-" + networkId + "-" + memberOrClassId2 + "-" + Long.toHexString(IdManager.oidMap.get("databaseOid")) + "-" + Long.toHexString(IdManager.oidMap.get("tableOid")) + "-" + Long.toHexString(IdManager.oidMap.get("tableOid")) + "-" + recordOid;
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
return existingId;
}
// Generate and store a new ID
String newId = IdManager.createNewFullURId(IdType.FULL_DHT_CLASS_UR_KEY, networkId, memberOrClassId2, request);
storeMapping(key, newId);
return newId;
}
// Generate or retrieve an ID for an entire Class_U Object
public static String generateFullDHTClassUKey(Cluster networkId, MemberOrClassId memberOrClassId, String databaseOid, String schemaOid, String tableOid) throws Exception {
// Key includes IdType to enforce independence or consistency as needed
String key = "FULL_DHT_CLASS_U_KEY-" + networkId + "-" + memberOrClassId + "-" + Long.toHexString(IdManager.oidMap.get("databaseOid")) + "-" + Long.toHexString(2200L) + "-" + Long.toHexString(IdManager.oidMap.get("schemaOid")) + "-" + Long.toHexString(IdManager.oidMap.get("tableOid"));
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
return existingId;
}
// Generate and store a new ID
String newId = IdManager.createAndStoreFullUId(IdType.FULL_DHT_TX_BLDER_META, networkId, memberOrClassId, request);
storeMapping(key, newId);
return newId;
}
/**public static String getSupernodeId(String networkId, MemberOrClassId identifier) {
System.out.println("Beginning to getSupernodeId()");
// Input validation
if (networkId == null || networkId.isEmpty()) {
throw new IllegalArgumentException("networkId cannot be null or empty");
}
System.out.println("Identifier passed: " + identifier);
if (identifier == null) {
throw new IllegalArgumentException("identifier cannot be null or empty");
}
// Construct the key
String delimiter = "-";
if (networkId.contains(delimiter)) {
throw new IllegalArgumentException("Inputs should not contain the delimiter: " + delimiter);
}
String key = "PEER_NODE_SUPERNODE" + delimiter + networkId + delimiter + identifier;
try {
System.out.println("About to determine if SupernodeId() already exists.");
System.out.flush();
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
System.out.println("SupernodeId() already exists!");
System.out.flush();
return existingId;
}
System.out.println("About to create NEW SupernodeId().");
System.out.flush();
// Generate and store a new ID
String newId = IdManager.createNewIdNonDb(
idType,
networkId,
identifier
);
//storeMapping(key, newId);
System.out.printf("Generated and stored new supernode ID = %s%n", newId);
System.out.flush();
return newId;
} catch (Exception e) {
System.err.println("An exception occurred: " + e.getMessage());
e.printStackTrace();
// Depending on your application's requirements, you might want to rethrow the exception
// or return a specific error value. Here, we'll return null to indicate failure.
return null;
}
}**/
// Generate or retrieve an ID for a secondary node
public static String getSecondaryNodeId(IdType idType, Cluster networkId2, MemberOrClassId identifier) {
System.out.println("Beginning to getSecondaryNodeId()");
// Input validation
if (networkId2 == null) {
throw new IllegalArgumentException("networkId cannot be null or empty");
}
if (identifier == null) {
throw new IllegalArgumentException("identifier cannot be null or empty");
}
// Construct the key
String delimiter = "-";
String key = "01" + delimiter + IdManager.getNetworkIdFromEnv().toString() + delimiter + IdManager.getMemberOrClassIdFromEnv().toString();
try {
System.out.println("About to determine if SecondaryNodeId() already exists.");
System.out.flush();
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
System.out.println("SecondaryNodeId() already exists!");
System.out.flush();
return existingId;
}
System.out.println("About to create NEW SecondaryNodeId().");
System.out.flush();
// Generate and store a new ID
String newId = IdManager.createNewIdNonDb(
idType,
networkId2,
memberOrClassId
);
storeMapping(key, newId);
System.out.printf("Generated and stored new secondary node ID = %s%n", newId);
System.out.flush();
return newId;
} catch (Exception e) {
System.err.println("An exception occurred: " + e.getMessage());
e.printStackTrace();
// Depending on your application's requirements, you might want to rethrow the exception
// or return a specific error value. Here, we'll return null to indicate failure.
return null;
}
}
// Generate or retrieve an ID for a tertiary node
public static String getTertiaryNodeId(IdType idType, Cluster networkId, MemberOrClassId identifier) {
System.out.println("Beginning to getSupernodeId()");
// Input validation
if (networkId == null) {
throw new IllegalArgumentException("networkId cannot be null or empty");
}
if (identifier == null) {
throw new IllegalArgumentException("identifier cannot be null or empty");
}
// Construct the key
String delimiter = "-";
String key = "02" + delimiter + networkId + delimiter + identifier;
try {
System.out.println("About to determine if TertiaryNodeId() already exists.");
System.out.flush();
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
System.out.println("TertiaryNodeId() already exists!");
System.out.flush();
return existingId;
}
System.out.println("About to create NEW tertiary nodeId().");
System.out.flush();
// Generate and store a new ID
String newId = IdManager.createNewIdNonDb(
idType,
networkId,
memberOrClassId
);
storeMapping(key, newId);
System.out.printf("Generated and stored new tertiary node ID = %s%n", newId);
System.out.flush();
return newId;
} catch (Exception e) {
System.err.println("An exception occurred: " + e.getMessage());
e.printStackTrace();
// Depending on your application's requirements, you might want to rethrow the exception
// or return a specific error value. Here, we'll return null to indicate failure.
return null;
}
}
// Generate or retrieve an ID for DhtTxBuilderMetadata
public static String getDhtTxBuilderMetadataId(String networkId, MemberOrClassId memberOrClassId) throws NoSuchAlgorithmException, InterruptedException, ExecutionException, TimeoutException {
// Use a common key format for databases, independent of Class
String key = "DHT_TX_BLDER_META-" + networkId + "-" + memberOrClassId;
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
return existingId;
}
// Generate and store a new ID
String newId = IdManager.createNewId(IdType.FULL_DHT_TX_BLDER_META, networkId, memberOrClassId, databaseOid, schemaOid, tableOid, recordOid);
storeMapping(key, newId);
return newId;
}
// Generate or retrieve an ID for a DhtTxTraceMetadata
public static String getDhtTxTraceMetadataId(String networkId, MemberOrClassId memberOrClassId) throws NoSuchAlgorithmException, InterruptedException, ExecutionException, TimeoutException {
// Use a common key format for databases, independent of Class
String key = "DHT_TX_TRACE_META-" + networkId + "-" + memberOrClassId;
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
return existingId;
}
// Generate and store a new ID
String newId = IdManager.createNewId(IdType.FULL_DHT_TX_BLDER_META, networkId, memberOrClassId, databaseOid, schemaOid, tableOid, recordOid);
storeMapping(key, newId);
return newId;
}
// Generate or retrieve an ID for a Role
public static String getRoleId(String networkId, MemberOrClassId memberOrClassId) throws NoSuchAlgorithmException, InterruptedException, ExecutionException, TimeoutException {
String key = "ROLE-" + networkId + "-" + memberOrClassId;
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
return existingId;
}
// Generate and store a new ID
String newId = IdManager.createNewId(IdType.ROLE, networkId, memberOrClassId, databaseOid, schemaOid, tableOid, recordOid);
storeMapping(key, newId);
return newId;
}
// Generate or retrieve an ID for a Client
public static String getClientId(Cluster networkId, MemberOrClassId base58PubKey) throws NoSuchAlgorithmException, InterruptedException, ExecutionException, TimeoutException {
String key = "CLIENT_PERSONAL-" + "0000000" + "-" + base58PubKey;
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
return existingId;
}
// Generate and store a new ID
String newId = IdManager.createNewIdNonDb(idType, networkId, memberOrClassId);
storeMapping(key, newId);
return newId;
}
// Generate or retrieve an ID for a Client
public static String getDhtValueId(Cluster networkId, MemberOrClassId memberOrClassId) throws NoSuchAlgorithmException, InterruptedException, ExecutionException, TimeoutException {
// Use a common key format for databases, independent of Class
String key = "DHT_VALUE-" + networkId + "-" + memberOrClassId;
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
return existingId;
}
// Generate and store a new ID
String newId = IdManager.createNewIdNonDb(IdType.DHT_VALUE, networkId, memberOrClassId);
storeMapping(key, newId);
return newId;
}
public static void main(String[] args) {
try {
// Example: Generate a database ID
String supernodeId = getSupernodeId(idType, networkId, memberOrClassId);
System.out.println("Database ID: " + supernodeId);
// Example: Generate a schema ID
String secondaryNodeId = getSecondaryNodeId(idType, networkId, memberOrClassId);
System.out.println("Schema ID: " + secondaryNodeId);
// Example: Generate a table ID for Class U
String tertiaryNodeId = getTertiaryNodeId(idType, networkId, memberOrClassId);
System.out.println("Table ID (Class U): " + tertiaryNodeId);
// Example: Generate a table ID for Class A
String typeAKey = IdManager.createNewFullAId(FULL_CLASS_A_KEY, networkId, memberOrClassId, request);
System.out.println("Full ID (Class A): " + typeAKey);
// Example: Generate a record ID for Class UR
String typeURKey = IdManager.createNewFullURId(FULL_CLASS_UR_KEY, networkId, memberOrClassId, request);
System.out.println("Full ID (Class UR): " + typeURKey);
// Example: Generate a table ID for Class A
String typeUKey = IdManager.createAndStoreFullUId(FULL_CLASS_U_KEY, networkId, memberOrClassId, request);
System.out.println("Table ID (Class U): " + typeUKey);
} catch (Exception e) {
System.err.println("Error: " + e.getMessage());
}
}
public static String getSupernodeId(IdType idType, Cluster networkId2, MemberOrClassId supernodeClass) {
System.out.println("Beginning to getSupernodeId()");
// Input validation
if (idType == null) {
throw new IllegalArgumentException("idType cannot be null");
}
if (networkId2 == null) {
throw new IllegalArgumentException("networkId cannot be null or empty");
}
if (supernodeClass == null) {
throw new IllegalArgumentException("supernodeClass (identifier) cannot be null");
}
System.out.println("Identifier passed: " + supernodeClass); // ✅ Use supernodeClass directly
// Construct the key
String delimiter = "-";
String networkHex = IdManager.getNetworkIdFromEnv().getId7(); // 👈 This gives you the 7-char hex
String key = "00" + delimiter + IdManager.getNetworkIdFromEnv().getId7() + delimiter + supernodeClass.getIdentifier();
try {
System.out.println("About to determine if SupernodeId() already exists.");
System.out.flush();
// Check if ID already exists
String existingId = getMapping(key);
if (existingId != null) {
System.out.println("SupernodeId() already exists!");
System.out.flush();
return existingId;
}
System.out.println("About to create NEW SupernodeId().");
System.out.flush();
// Generate and store a new ID
String newId = IdManager.createNewIdNonDb(
idType,
networkId2,
supernodeClass
);
//storeMapping(key, newId);
System.out.printf("Generated and stored new supernode ID = %s%n", newId);
System.out.printf("Generated supernodeIdStr = %s, networkId = %s, supernodeClass (memberOrClassId) = %s%n",
newId, IdManager.getNetworkIdFromEnv().getId7(), supernodeClass.getIdentifier());
System.out.flush();
return newId;
} catch (Exception e) {
System.err.println("An exception occurred in getSupernodeId(): " + e.getMessage());
e.printStackTrace();
return null;
}
}
}
package bftsmart.demo.itotcca;
import java.math.BigInteger;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.X509EncodedKeySpec;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HexFormat;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import io.etcd.jetcd.ByteSequence;
import io.etcd.jetcd.Client;
import io.etcd.jetcd.KV;
import io.etcd.jetcd.kv.GetResponse;
import io.etcd.jetcd.options.GetOption;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import jakarta.xml.bind.DatatypeConverter;
import org.bouncycastle.jcajce.provider.digest.SHA3;
import org.elastos.did.crypto.Base58;
import bftsmart.demo.itotcca.CryptoBox.PublicKey;
public class IdManager {
// bftsmart.demo.itotcca.IdManager.Id object representation
public static class Id implements Comparable {
protected static final int SIZE = 512;
public static final int BYTES = SIZE / Byte.SIZE; // Should be 64 (512/8)
public static final int ID_BYTE_LENGTH = 64;
public static Id MAX_ID;
public static Id MIN_ID;
/**static {
try {
System.out.println("[DEBUG] Initializing MAX_ID with IdType.DHT_VALUE...");
byte[] maxIdBytes = new byte[BYTES];
Arrays.fill(maxIdBytes, (byte) 0xFF);
MAX_ID = new Id(maxIdBytes, IdType.DHT_VALUE);
MIN_ID = new Id(new byte[BYTES], IdType.DHT_VALUE);
System.out.println("[DEBUG] MAX_ID and MIN_ID initialized successfully.");
} catch (Exception e) {
System.err.println("[ERROR] Failed to initialize MAX_ID or MIN_ID: " + e.getMessage());
throw new RuntimeException("Failed to initialize MAX_ID or MIN_ID", e);
}
}
static {
try {
byte[] maxIdBytes = new byte[BYTES];
Arrays.fill(maxIdBytes, (byte) 0xFF);
MAX_ID = new Id(maxIdBytes, IdType.DHT_VALUE);
MIN_ID = new Id(new byte[BYTES], IdType.DHT_VALUE);
} catch (Exception e) {
throw new RuntimeException("Failed to initialize MAX_ID or MIN_ID", e);
}
}**/
protected IdType type;
protected byte[] value;
protected static byte[] bytes; // This holds the 64-byte (512-bit) ID
protected int length;
private String nodeId;
private long lastSeen;
private IdManager.Id id;
public Id(byte[] idBytes, IdType type) {
if (idBytes == null || idBytes.length != 64) {
throw new IllegalArgumentException("ID must be 64 bytes long (SHA3-512)");
}
if (type == null) {
throw new IllegalArgumentException("IdType cannot be null.");
}
this.bytes = Arrays.copyOf(idBytes, idBytes.length);
this.type = type;
}
public Id(String string) {
if (string == null) {
throw new IllegalArgumentException("Prefix cannot be null.");
}
// Assume the Prefix class provides a method to convert itself to a byte array or ID representation.
byte[] prefixBytes = string.getBytes();
if (prefixBytes.length != BYTES) { // Must check BYTES, not SIZE
throw new IllegalArgumentException("Prefix bytes must match the required ID byte size (64 bytes).");
}
this.bytes = Arrays.copyOf(prefixBytes, BYTES);
this.type = null;
this.value = new byte[0];
}
// Calculate XOR distance between two IDs
public static byte[] distance(byte[] id2, byte[] bs2) {
if (id2 == null || bs2 == null) {
throw new IllegalArgumentException("Ids cannot be null.");
}
byte[] bytes1 = id2; // ✅ Convert Id to byte array
byte[] bytes2 = bs2;
if (bytes1.length != bytes2.length) {
throw new IllegalArgumentException("IDs must have the same length.");
}
byte[] resultBytes = new byte[bytes1.length];
for (int i = 0; i < bytes1.length; i++) {
resultBytes[i] = (byte) (bytes1[i] ^ bytes2[i]); // ✅ XOR each byte
}
return resultBytes; // ✅ Create a new Id with XOR result
}
// Generate a zero ID (all bytes set to 0)
public static Id zero() {
return new Id(new byte[BYTES], IdType.DHT_VALUE);
}
public static byte[] fromBase58(String base58) {
return base58.getBytes();
}
// ✅ Fix getBytes() to ensure immutability
public byte[] getBytes() {
return Arrays.copyOf(bytes, bytes.length);
}
// ✅ Correct compareTo method
public int compareTo(Id other) {
if (other == null) {
System.err.println("compareTo: other Id is NULL!");
return 1; // Assume "this" is greater if comparing with null
}
if (other.bytes == null) {
System.err.println("compareTo: other Id bytes are NULL!");
return 1; // Assume "this" is greater if comparing with null
}
return Arrays.compareUnsigned(this.bytes, other.bytes);
}
// ✅ Fix toHexString() method
public String toHexString() {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
hexString.append(String.format("%02x", b));
}
return hexString.toString();
}
public IdType getType() {
return type;
}
public byte[] getValue() {
return value;
}
public boolean matchesType(IdType expectedType) {
return type == expectedType;
}
public static Id ofHex(String supernodeNodeId) {
if (supernodeNodeId.length() != 128) {
throw new IllegalArgumentException("Hex string must be 128 characters long to represent a 64-byte ID.");
}
byte[] decodedBytes = (supernodeNodeId).getBytes();
try {
return new Id(bytes, IdType.DHT_VALUE);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // ✅ or another valid IdType
return MAX_ID;
}
private static byte[] decodeHex(byte[] workerNodeId) {
if (workerNodeId.length != 128) { // Validate length for a 64-byte ID (512 bits)
throw new IllegalArgumentException("Hex string must be 128 characters long to represent a 64-byte ID.");
}
int length = workerNodeId.length;
byte[] data = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
data[i / 2] = (byte) ((Character.digit(workerNodeId.length, 16) << 4)
+ Character.digit(workerNodeId.length, 16));
}
return data;
}
/**
* sorts the closest entries to the head, the furth est to the tail
*/
public static final class Comparator implements java.util.Comparator {
private final Id target;
public Comparator(Id target) {
this.target = target;
}
public int compare(int o1, IdManager.Id o2) {
return (int) target.threeWayCompare(o1, o2);
}
@Override
public int compare(Id arg0, Id arg1) {
// TODO Auto-generated method stub
return 0;
}
}
/**
* Construct a random kademlia Id.
*/
protected Id() {
bytes = new byte[BYTES];
this.idBytes = null;
}
public int threeWayCompare(int o1, Id o2) {
// TODO Auto-generated method stub
return 0;
}
/**
* Construct a random kademlia Id.
*/
protected Id(Id id) {
new Id(bytes, IdType.DHT_VALUE); ;
}
public static Id maxId(IdType type) {
byte[] maxIdBytes = new byte[BYTES];
Arrays.fill(maxIdBytes, (byte) 0xFF);
return new Id(maxIdBytes, type);
}
public static Id minId(IdType type) {
return new Id(new byte[BYTES], type);
}
/**
* Construct the kademlia Id from a given binary id
*
* @param id the binary id in bytes
*/
protected Id(byte[] buf, int pos) {
this.bytes = Arrays.copyOfRange(buf, pos, pos + BYTES);
this.idBytes = null;
}
public static Id of(byte[] buf, int pos) {
if (buf.length - pos < BYTES)
throw new IllegalArgumentException("Binary id should be " + BYTES + " bytes long.");
return new Id(buf, pos);
}
public static String of(String id) {
if (id.startsWith("0x")) return Arrays.toString(id.getBytes());
return ofBase58(id);
}
/**
* Create a Carrier Id from base58 string.
*
* @param base58Id the id string
*/
public static String ofBase58(String base58Id) {
return of(Base58.decode(base58Id));
}
public static Id random(IdType idType, bftsmart.demo.itotcca.Cluster networkId2, MemberOrClassId memberOrClassId) {
if (networkId2 == null || memberOrClassId == null) {
throw new IllegalArgumentException("Network ID and Member/Class ID cannot be null.");
}
// Generate 64-bit (8-byte) prefix
byte[] prefixBytes = generateNonDbIdPrefix(idType, networkId2, memberOrClassId);
// Generate 448-bit (56-byte) random portion
byte[] randomBytes = new byte[56];
SecureRandom secureRandom = new SecureRandom(); // Ensure fresh randomness
secureRandom.nextBytes(randomBytes); // Fill with truly random bytes
// Concatenate prefix + random part
byte[] fullIdBytes = new byte[64]; // 512 bits = 64 bytes
System.arraycopy(prefixBytes, 0, fullIdBytes, 0, 8); // Copy prefix (8 bytes)
System.arraycopy(randomBytes, 0, fullIdBytes, 8, 56); // Copy random part (56 bytes)
return new Id(fullIdBytes, idType);
}
private static final int RECORD_ID_BITS = 150;
private static final int RECORD_ID_BYTES = (int) Math.ceil(RECORD_ID_BITS / 8.0); // 19 bytes
public static String randomRecordIdHex() {
// Generate 19 random bytes
byte[] randomBytes = new byte[RECORD_ID_BYTES];
SecureRandom secureRandom = new SecureRandom(); // Cryptographically strong random source
secureRandom.nextBytes(randomBytes); // Fill the byte array with secure random data
// Convert to Hex String (uppercase for clarity)
return HexFormat.of().formatHex(randomBytes).toUpperCase();
}
public static void main(String[] args) {
// Test: Generate a random 150-bit hex string
String randomHexId = randomRecordIdHex();
System.out.println("Random 150-bit Record ID (Hex): " + randomHexId);
}
public byte[] bytes() {
return bytes;
}
public int getInt(int offset) {
return Byte.toUnsignedInt(bytes[offset]) << 24 |
Byte.toUnsignedInt(bytes[offset+1]) << 16 |
Byte.toUnsignedInt(bytes[offset+2]) << 8 |
Byte.toUnsignedInt(bytes[offset+3]);
}
public Id add(Id id) {
Id result = new Id();
byte[] a = bytes;
byte[] b = id.bytes;
byte[] r = result.bytes;
int carry = 0;
for(int i = BYTES - 1; i >= 0; i--) {
carry = (a[i] & 0xff) + (b[i] & 0xff) + carry;
r[i] = (byte)(carry & 0xff);
carry >>>= 8;
}
return result;
}
/**
* Checks the distance between this and another Id
*
* @param id another Id
*
* @return The distance of this NodeId from the given NodeId
*/
public byte[] distance(byte[] to) {
return distance(this, to);
}
private byte[] distance(Id id2, byte[] to) {
if (id2 == null || to == null) {
throw new IllegalArgumentException("IDs cannot be null.");
}
byte[] idBytes = id2.getBytes(); // Convert Id to byte array
if (idBytes.length != to.length) {
throw new IllegalArgumentException("Byte arrays must have the same length.");
}
byte[] result = new byte[idBytes.length];
for (int i = 0; i < idBytes.length; i++) {
result[i] = (byte) (idBytes[i] ^ to[i]); // Perform XOR operation byte-by-byte
}
return result;
}
/**
* Get an Id that is some distance away from this Id
*
* @param distance in number of bits
* @return the newly generated Id
*/
public byte[] getIdByDistance(int distance) {
byte[] result = new byte[BYTES];
int zeroBytes = (SIZE - distance) / 8;
int zeroBits = (SIZE - distance) % 8;
// new byte array is initialized with all zeroes
// Arrays.fill(result, 0, zeroBytes, (byte)0);
if (zeroBytes < BYTES) {
result[zeroBytes] = (byte)(0xFF >>> zeroBits);
Arrays.fill(result, zeroBytes + 1, BYTES, (byte) 0xFF);
}
return Id.of(result).getBytes();
}
/**
* Counts the number of leading 0's in this Id
*
* @return the number of leading 0's
*/
public int getLeadingZeros() {
int msb = 0;
int i;
for (i = 0; i < BYTES && bytes[i] == 0; i++);
msb += i << 3;
if (i < BYTES) {
byte b = bytes[i];
if (b > 0) {
int n = 7;
if (b >= 1 << 4) { n -= 4; b >>>= 4; }
if (b >= 1 << 2) { n -= 2; b >>>= 2; }
msb += (n - (b >>> 1));
}
}
return msb;
}
/**
* Counts the number of trailing 0's in this Id
*
* @return the number of trailing 0's
*/
public int getTrailingZeros() {
int lsb = 0;
int i;
for (i = BYTES - 1; i >= 0 && bytes[i] == 0; i--);
lsb += (BYTES - 1 - i) << 3;
if (i >= 0) {
byte b = (byte)(~bytes[i] & (bytes[i] - 1));
if (b <= 0) {
lsb += (b & 8);
} else {
if (b > 1 << 4) { lsb += 4; b >>>= 4; }
if (b > 1 << 2) { lsb += 2; b >>>= 2; }
lsb += ((b >>> 1) + 1);
}
}
return lsb;
}
/**
* Check if the leading bits up to the Nth bit of both Id are equal
*
*
* n = -1: no bits have to match
* n >= 0: n bytes have to match
*
*/
protected static boolean bitsEqual(Id id1, Id id2, int n) {
if (n < 0)
return true;
int mmi = Arrays.mismatch(id1.bytes, id2.bytes);
int indexToCheck = n >>> 3;
int diff = (id1.bytes[indexToCheck] ^ id2.bytes[indexToCheck]) & 0xff;
boolean bitsDiff = (diff & (0xff80 >>> (n & 0x07))) == 0;
return mmi == indexToCheck ? bitsDiff : Integer.compareUnsigned(mmi, indexToCheck) > 0;
}
protected static void bitsCopy(Id src, Id dest, int depth) {
if (depth < 0)
return;
// copy over all complete bytes
int idx = depth >>> 3;
if (idx > 0)
System.arraycopy(src.bytes, 0, dest.bytes, 0, idx);
int mask = 0xFF80 >>> (depth & 0x07);
// mask out the part we have to copy over from the last prefix byte
dest.bytes[idx] &= ~mask;
// copy the bits from the last byte
dest.bytes[idx] |= src.bytes[idx] & mask;
}
public Signature.PublicKey toSignatureKey() {
return Signature.PublicKey.fromBytes(bytes);
}
/**
* Compares a NodeId to this NodeId
*
* @param o The NodeId to compare to this NodeId
* @return boolean Whether the 2 NodeIds are equal
*/
@Override
public boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Id) {
Id id = (Id) o;
return Arrays.equals(this.bytes, id.bytes);
}
return false;
}
@Override
public int hashCode() {
byte[] b = bytes;
return (((b[0] ^ b[1] ^ b[2] ^ b[3] ^ b[4] ^ b[5] ^ b[6] ^ b[7]) & 0xff) << 24)
| (((b[8] ^ b[9] ^ b[10] ^ b[11] ^ b[12] ^ b[13] ^ b[14] ^ b[15]) & 0xff) << 16)
| (((b[16] ^ b[17] ^ b[18] ^ b[19] ^ b[20] ^ b[21] ^ b[22] ^ b[23]) & 0xff) << 8)
| ((b[24] ^ b[25] ^ b[26] ^ b[27] ^ b[28] ^ b[29] ^ b[30] ^ b[31]) & 0xff);
}
/**
* @return The BigInteger representation of the key
*/
public BigInteger toInteger() {
return new BigInteger(1, bytes);
}
public String toBase58String() {
return Base58.encode(bytes);
}
public String toBinaryString() {
StringBuilder repr = new StringBuilder(SIZE + (SIZE >>> 2));
for(int i = 0; i < SIZE; i++) {
repr.append((bytes[i >>> 3] & (0x80 >> (i & 0x07))) != 0 ? '1' : '0');
if ((i & 0x03) == 0x03) repr.append(' ');
}
return repr.toString();
}
public boolean isRecordId() {
// Check if the ID's first 3 bits correspond to the ENTERPRISE_RECORD prefix "100"
return (bytes[0] & 0xE0) == 0x80; // 0xE0 masks the first 3 bits, 0x80 is binary 10000000
}
public boolean isNodeId() {
return (bytes[0] & 0xE0) == 0x00; // Check if the first 3 bits are 000
}
public boolean isValueId() {
// Check if the first 3 bits of the first byte are "111" (binary)
return (bytes[0] & 0xE0) == 0xE0;
}
public boolean isPeerId() {
// Check if the first 3 bits of the first byte are "000" (binary)
return (bytes[0] & 0xE0) == 0x00;
}
public static bftsmart.demo.itotcca.Id of(bftsmart.demo.itotcca.Id id2) {
if (id2 == null || id2.length != 64) {
throw new IllegalArgumentException("Invalid byte array for Id.");
}
return (bftsmart.demo.itotcca.Id) id2;
}
public static bftsmart.demo.itotcca.Prefix of(Prefix prefix) {
if (prefix == null || prefix.length != 64) {
throw new IllegalArgumentException("Invalid byte array for Id.");
}
return (bftsmart.demo.itotcca.Prefix) prefix;
}
public boolean isEnterpriseRecord() {
return (bytes[0] & 0xE0) == 0x80;
}
public boolean isTransactionTrace() {
return (bytes[0] & 0xE0) == 0xA0;
}
public boolean isTableId() {
return (bytes[0] & 0xE0) == 0x40;
}
public static Id ofBit(int depth) {
if (depth < 0 || depth >= SIZE * 8) {
throw new IllegalArgumentException("Depth must be in the range [0, 511].");
}
byte[] idBytes = new byte[SIZE];
int byteIndex = depth / 8;
int bitIndex = 7 - (depth % 8);
idBytes[byteIndex] = (byte) (1 << bitIndex);
return new Id(idBytes, IdType.DHT_VALUE);
}
// Compare this Id with the first Id
int compareToFirst = this.compareTo((IdManager.Id) id);
// Compare this Id with the last Id
int compareToLast = this.compareTo((IdManager.Id) id);
public static byte[] hashValue(String input) {
// TODO Auto-generated method stub
return null;
}
public PublicKey toEncryptionKey() {
try {
X509EncodedKeySpec spec = new X509EncodedKeySpec(this.bytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return (PublicKey) keyFactory.generatePublic(spec);
} catch (Exception e) {
throw new RuntimeException("Failed to convert Id to PublicKey: " + e.getMessage(), e);
}
}
public static void bitsCopy(Prefix source, Id firstId, int depth) {
if (source == null || firstId == null) {
throw new IllegalArgumentException("Source and destination prefixes cannot be null.");
}
byte[] sourceBytes = source.getBytes();
byte[] destinationBytes = firstId.getBytes();
System.arraycopy(sourceBytes, 0, destinationBytes, 0, depth / 8); // Copy full bytes
int remainingBits = depth % 8;
if (remainingBits > 0) {
int mask = 0xFF00 >>> remainingBits; // Mask for remaining bits
destinationBytes[depth / 8] &= ~mask; // Clear the remaining bits
destinationBytes[depth / 8] |= sourceBytes[depth / 8] & mask; // Set bits from source
}
}
public Object threeWayCompare(byte[] id1, byte[] id2) {
// TODO Auto-generated method stub
return null;
}
public static bftsmart.demo.itotcca.IdManager.Id of(bftsmart.demo.itotcca.IdManager.Id id2) {
if (id2 == null || id2.length != 64) {
throw new IllegalArgumentException("Invalid byte array for Id.");
}
return (bftsmart.demo.itotcca.IdManager.Id) id2;
}
public static String of(byte[] keyBytes) {
if (keyBytes == null || keyBytes.length == 0) {
throw new IllegalArgumentException("Key bytes must not be null or empty.");
}
// Ensure the key size is correct (e.g., 64 bytes for SHA3-512 IDs)
if (keyBytes.length != SIZE) { // SIZE must be defined elsewhere
throw new IllegalArgumentException("Invalid key length. Expected " + SIZE + " bytes.");
}
// ✅ Simply return the original keyBytes (no need to wrap in Id object)
return Arrays.toString(keyBytes.clone()); // Returns a defensive copy to prevent modification
}
public String toHex() {
StringBuilder hexString = new StringBuilder();
byte[] idBytes = this.getBytes(); // Assuming `getBytes()` returns the ID as a byte array.
for (byte b : idBytes) {
hexString.append(String.format("%02x", b));
}
return hexString.toString();
}
public static Id from(bftsmart.demo.itotcca.Id sender) {
if (sender == null) {
throw new IllegalArgumentException("Sender ID cannot be null.");
}
// Assuming that the `Id` class can directly use the bytes from `sender`
byte[] senderBytes = sender.getBytes(); // Ensure the `Id` class has a `getBytes()` method
return new Id(senderBytes, IdType.DHT_VALUE);
}
public Id getId() {
return this; // Return the current Id instance
}
public boolean isValidForLookup() {
// Check if the node is reachable and active
if (!isOnline() || !isReachable()) {
return false;
}
// Additional criteria for validity can be added here
// Example: Ensure the node has a valid ID and is not marked as stale
return this.nodeId != null && !isStale();
}
public boolean isReachable() {
// TODO Auto-generated method stub
return false;
}
private boolean isOnline() {
// TODO Auto-generated method stub
return false;
}
// Example auxiliary methods that could support the logic
private boolean isStale() {
// Assume we have a `lastSeen` timestamp and a threshold
long currentTime = BFTTimeService.getLeaderTime();
long staleThreshold = 5 * 60 * 1000; // 5 minutes
return (currentTime - this.lastSeen) > staleThreshold;
}
public static String toHexString(IdManager.Id id) {
if (id == null || id.length == 0) {
throw new IllegalArgumentException("Byte array cannot be null or empty.");
}
return toHexString(id);
}
public int calculateDistance(bftsmart.demo.itotcca.IdManager.Id nodeId) {
// TODO Auto-generated method stub
return 0;
}
// Factory method for special cases
public static Id emptyId() {
return new Id(new byte[0], IdType.DHT_VALUE);
}
private byte[] idBytes;
public Id(Id maxIdBytes2, IdType dhtValue) {
this.idBytes = null;
// TODO Auto-generated constructor stub
}
public Id(IdType type2, String hexString) {
if (type2 == null) {
throw new IllegalArgumentException("IdType cannot be null.");
}
if (hexString == null || hexString.length() != 2 * BYTES) {
throw new IllegalArgumentException("Hex string must be " + (2 * BYTES) + " characters long.");
}
this.bytes = decodeHex(hexString.getBytes());
MAX_ID.type = type2;
}
/**
* Converts the ID into a byte array representation.
* @return Byte array of the ID.
*/
public byte[] toByteArray() {
return Arrays.copyOf(idBytes, idBytes.length);
}
/**
* Converts the ID into a BigInteger for mathematical operations.
* @return BigInteger representation of the ID.
*/
public BigInteger toBigInteger() {
return new BigInteger(1, idBytes);
}
public int getBitLength() {
return idBytes.length * 8; // 64 bytes * 8 bits = 512 bits
}
public static bftsmart.demo.itotcca.KBucketEntry ofHexK(String nodeIdStr) {
if (nodeIdStr.length() != 128) {
throw new IllegalArgumentException("Hex string must be 128 characters long to represent a 64-byte ID.");
}
byte[] decodedBytes = decodeHex(nodeIdStr.getBytes());
return new bftsmart.demo.itotcca.KBucketEntry(decodedBytes);
}
public static Id fromBytes(byte[] byteArray) {
if (byteArray == null || byteArray.length != 64) { // Assuming 512-bit (64-byte) Ids
throw new IllegalArgumentException("Invalid byte array length. Expected 64 bytes (512-bit Id).");
}
try {
// Convert byte array to a hexadecimal string representation
String hexString = DatatypeConverter.printHexBinary(byteArray).toLowerCase();
// Create and return a new Id object
return new Id(IdType.DHT_VALUE, hexString);
} catch (Exception e) {
throw new RuntimeException("Error parsing byte array to Id", e);
}
}
public String approxDistance(Id target, Id target2) {
if (target == null || target2 == null) {
throw new IllegalArgumentException("Target IDs cannot be null.");
}
byte[] bytes1 = target.getBytes();
byte[] bytes2 = target2.getBytes();
int diffCount = 0;
// XOR each byte to count bit differences
for (int i = 0; i < bytes1.length; i++) {
diffCount += Integer.bitCount(bytes1[i] ^ bytes2[i]); // Count differing bits
}
return String.valueOf(diffCount); // Return as a string
}
public int compareTo(Prefix prefix) {
// TODO Auto-generated method stub
return 0;
}
public int threeWayCompare(Id id1, Id id2) {
// TODO Auto-generated method stub
return 0;
}
public int threeWayCompare(byte[] lastKey, Id neighborInfo) {
// TODO Auto-generated method stub
return 0;
}
public Id distance(Id maxId, Id target) {
// TODO Auto-generated method stub
return null;
}
public byte[] getAddress() {
// TODO Auto-generated method stub
return null;
}
}
private static final String ETCD_ENDPOINTS = System.getenv("ETCD_ENDPOINTS");
private static Client etcdClient = initializeEtcdClient();
private static IdType idType;
public static Map oidMap;
private static String databaseOid;
private static String schemaOid;
private static String tableOid;
private static String fullTableName;
private static String recordOid;
private static String recordId;
private static byte[] suffixBytes;
private static Object result;
private static String containerName;
private static String txId;
private static int entnum;
private static long slabId;
private static String networkId;
private static String databaseId;
private static String schemaId;
private static String tableId;
private static String blockId;
private static String value;
private static URI[] initializeEtcdEndpoints() {
String endpoints = ETCD_ENDPOINTS;
return Arrays.stream(endpoints.split(","))
.map(String::trim)
.map(URI::create)
.toArray(URI[]::new);
}
// Method to initialize the Etcd client
public static Client initializeEtcdClient() {
URI[] etcdEndpoints = initializeEtcdEndpoints();
try {
// Build and return the Etcd client
return Client.builder()
.endpoints(etcdEndpoints)
.build();
} catch (Exception e) {
throw new IllegalStateException("Failed to initialize Etcd client: " + e.getMessage(), e);
}
}
private static final String clusterName = System.getenv("CLUSTER");
public static bftsmart.demo.itotcca.Cluster parseCluster(String clusterName) {
if (clusterName == null || clusterName.isBlank()) {
throw new IllegalArgumentException("❌ Environment variable 'CLUSTER' is missing or empty!");
}
try {
// Convert environment variable to uppercase for Enum lookup
return bftsmart.demo.itotcca.Cluster.valueOf(clusterName.toUpperCase());
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("❌ Invalid cluster name: " + clusterName, e);
}
}
// If you need lowercase for internal use:
public static String getClusterNameLowerCase() {
return parseCluster(clusterName).name().toLowerCase(); // ✅ Now safely converts enum name to lowercase
}
public static void mapTableOidToUseCase(String networkId, String database, String schemaName, String tableName) throws Exception {
// Retrieve OIDs from Redis cache or PostgreSQL
Map oidMap = getOIDs(networkId, database, schemaName, tableName);
if (!oidMap.containsKey("databaseOid") || !oidMap.containsKey("schemaOid") || !oidMap.containsKey("tableOid")) {
throw new RuntimeException("Failed to retrieve necessary OIDs for mapping to use case.");
}
// Extract OIDs
long databaseOid = oidMap.get("databaseOid");
long schemaOid = oidMap.get("schemaOid");
long tableOid = oidMap.get("tableOid");
// Construct Use Case mapping keys
String keyDatabase = "dbToUseCase:" + databaseOid;
String keySchema = "schemaToUseCase:" + schemaOid;
String keyTable = "tableToUseCase:" + tableOid;
// Convert OIDs to byte[]
byte[] databaseOidBytes = ByteBuffer.allocate(Long.BYTES).putLong(databaseOid).array();
byte[] schemaOidBytes = ByteBuffer.allocate(Long.BYTES).putLong(schemaOid).array();
byte[] tableOidBytes = ByteBuffer.allocate(Long.BYTES).putLong(tableOid).array();
// Store OIDs in etcd and Redis
putToEtcd(keyDatabase, databaseOidBytes);
putToEtcd(keySchema, schemaOidBytes);
putToEtcd(keyTable, tableOidBytes);
}
/**
* Retrieves both the database OID and the schema.table OID.
*
* @param networkId The network identifier.
* @param database The database name.
* @param schemaName The schema name.
* @param tableName The table name.
* @return A map containing the database OID and table OID.
* @throws Exception If a database access error occurs.
*/
public static Map getOIDs(String networkId, String database, String schemaName, String tableName) throws Exception {
Map oidMap = new TreeMap<>();
// If all OIDs are cached, return them
if (oidMap.containsKey("databaseOid") && oidMap.containsKey("schemaOid") && oidMap.containsKey("tableOid")) {
return oidMap;
}
String query = "SELECT d.oid AS database_oid, n.oid AS schema_oid, c.oid AS table_oid " +
"FROM pg_database d " +
"JOIN pg_namespace n ON n.nspname = ? " +
"JOIN pg_class c ON c.relnamespace = n.oid AND c.relname = ? " +
"WHERE d.datname = ?";
int replicaId = Integer.valueOf(System.getenv("NODE_ID"));
Connection[] connArr = DatabaseConnector.getConnection(parseCluster(clusterName));
try (Connection conn = connArr[replicaId];
PreparedStatement statement = conn.prepareStatement(query)) {
statement.setString(1, schemaName);
statement.setString(2, tableName);
statement.setString(3, database);
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
long dbOid = resultSet.getLong("database_oid");
long schemaOid = resultSet.getLong("schema_oid");
long tableOid = resultSet.getLong("table_oid");
oidMap.put("databaseOid", dbOid);
oidMap.put("schemaOid", schemaOid);
oidMap.put("tableOid", tableOid);
}
}
} catch (SQLException e) {
throw new RuntimeException("Failed to retrieve database, schema, and table OIDs from PostgreSQL", e);
}
return oidMap;
}
public static Id generateTransactionTraceId(String networkId, MemberOrClassId memberOrClassId, String database, String schema, String table, String recordId, UUID txId) {
// Validate inputs
if (recordId == null || recordId.isEmpty()) {
throw new IllegalArgumentException("Record ID must not be null or empty.");
}
// Retrieve the suffix stored in Redis/etcd
String cacheKey = "DHT_TX_TRACE_SUFFIX:" + database + "." + schema + "." + table + "." + recordId;
suffixBytes = getFromEtcd(cacheKey);
String suffix = (suffixBytes != null) ? new String(suffixBytes, StandardCharsets.UTF_8) : "DEFAULT_SUFFIX";
// Create a ByteBuffer with the required ID format
ByteBuffer buffer = ByteBuffer.allocate(96); // Ensure enough space for all fields
buffer.put((byte) 0b10100000);
buffer.putLong(Long.parseLong(networkId, 16)); // Convert hex networkId to long
buffer.putLong(Long.parseLong(memberOrClassId.toString(), 16));
buffer.putLong(Long.parseLong(database, 16));
buffer.putLong(Long.parseLong(schema, 16));
buffer.putLong(Long.parseLong(table, 16));
buffer.putLong(Long.parseLong(recordId, 16));
buffer.putLong(txId.getMostSignificantBits());
buffer.putLong(txId.getLeastSignificantBits());
// Append the suffix
byte[] suffixData = suffix.getBytes(StandardCharsets.UTF_8);
buffer.put(suffixData, 0, Math.min(suffixData.length, buffer.remaining()));
return new Id(buffer.array(), IdType.FULL_DHT_TX_BLDER_META);
}
public static Id getUseCaseMetadataId(GeneralMessage request, IdType idType) {
String key = "tableToUseCase:" + request.getFullTableName();
result = getFromEtcd(key);
if (result != null) {
String suffix = new String();
String useCaseId = "DHT_TX_BLDER_META-" + suffix;
return new Id(useCaseId.getBytes(StandardCharsets.UTF_8), IdType.FULL_DHT_TX_BLDER_META);
}
return null;
}
public static void putToEtcd(String key, byte[] value) {
try {
etcdClient.getKVClient().put(ByteSequence.from(key.getBytes()), ByteSequence.from(value)).get();
} catch (Exception e) {
throw new RuntimeException("Failed to put data to etcd", e);
}
}
private static byte[] getFromEtcd(String key) {
try {
GetResponse response = etcdClient.getKVClient().get(ByteSequence.from(key.getBytes()), GetOption.DEFAULT).get();
if (!response.getKvs().isEmpty()) {
return response.getKvs().get(0).getValue().getBytes();
}
} catch (Exception e) {
throw new RuntimeException("Failed to get data from etcd", e);
}
return null;
}
// Generate an ID
public static bftsmart.demo.itotcca.IdManager.Id generateId(IdType type, String input) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA3-512");
byte[] hash = digest.digest(input.getBytes());
String binaryHash = bytesToBinaryString(hash);
return new bftsmart.demo.itotcca.IdManager.Id(type, binaryHash.substring(3)); // Add prefix
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA3-512 algorithm not found", e);
}
}
public static Id generateId(IdType type, String networkId, MemberOrClassId memberOrClassId, String databaseOid, String schemaOid, String tableOid, String recordOid) {
try {
MessageDigest sha3 = MessageDigest.getInstance("SHA3-512");
String fullKey = type.getHexPrefix() + networkId + memberOrClassId + databaseOid + schemaOid + tableOid;
if (recordOid != null) fullKey += recordOid;
byte[] hashedId = sha3.digest(fullKey.getBytes(StandardCharsets.UTF_8));
return new Id(Arrays.copyOf(hashedId, 64), IdType.DHT_VALUE);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA3-512 algorithm not available.", e);
}
}
public static Map getOIDs(String database, String schema, String table) throws SQLException {
Map oidMap = new TreeMap<>();
String query = "SELECT d.oid AS database_oid, n.oid AS schema_oid, c.oid AS table_oid FROM pg_database d "
+ "JOIN pg_namespace n ON n.nspname = ? "
+ "JOIN pg_class c ON c.relnamespace = n.oid AND c.relname = ? "
+ "WHERE d.datname = ?";
int replicaId = Integer.valueOf(System.getenv("NODE_ID"));
Connection[] connArr = DatabaseConnector.getConnection(parseCluster(clusterName));
try (Connection conn = connArr[replicaId];
PreparedStatement statement = conn.prepareStatement(query)) {
statement.setString(1, schema);
statement.setString(2, table);
statement.setString(3, database);
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
oidMap.put("databaseOid", resultSet.getLong("database_oid"));
oidMap.put("schemaOid", resultSet.getLong("schema_oid"));
oidMap.put("tableOid", resultSet.getLong("table_oid"));
}
}
}
return oidMap;
}
public static String createFullClassAKey(String networkId, MemberOrClassId memberOrClassId, String database, String schema, String table, String record) throws SQLException {
Map oidMap = getOIDs(database, schema, table);
return generateId(IdType.RECORD_CLASS_A, networkId, memberOrClassId, Long.toHexString(oidMap.get("databaseOid")),
Long.toHexString(oidMap.get("schemaOid")), Long.toHexString(oidMap.get("tableOid")), record).toHexString();
}
public static String createFullClassUKey(String networkId, MemberOrClassId memberOrClassId, String database, String schema, String table) throws SQLException {
Map oidMap = getOIDs(database, schema, table);
return generateId(IdType.FULL_DHT_TX_BLDER_META, networkId, memberOrClassId, Long.toHexString(oidMap.get("databaseOid")),
Long.toHexString(oidMap.get("schemaOid")), Long.toHexString(oidMap.get("tableOid")), null).toHexString();
}
public static String createFullBFTClassURKey(String networkId, MemberOrClassId memberOrClassId, String database, String schema, String table, String record) throws SQLException {
Map oidMap = getOIDs(database, schema, table);
return generateId(IdType.RECORD_BFT_CLASS_UR, networkId, memberOrClassId, Long.toHexString(oidMap.get("databaseOid")),
Long.toHexString(oidMap.get("schemaOid")), Long.toHexString(oidMap.get("tableOid")), record).toHexString();
}
public static String createFullDHTClassURKey(String networkId, MemberOrClassId memberOrClassId, String database, String schema, String table, String record) throws SQLException {
Map oidMap = getOIDs(database, schema, table);
return generateId(IdType.RECORD_DHT_CLASS_UR, networkId, memberOrClassId, Long.toHexString(oidMap.get("databaseOid")),
Long.toHexString(oidMap.get("schemaOid")), Long.toHexString(oidMap.get("tableOid")), record).toHexString();
}
// Method to generate a new ID and return the result
public static String createNewId(IdType idType, String networkId, MemberOrClassId memberOrClassId, String databaseOid, String schemaOid, String tableOid, String recordOid) throws NoSuchAlgorithmException, InterruptedException, ExecutionException, TimeoutException {
System.out.println("We got this far - beginning to create new Id");
String identifier = memberOrClassId.getIdentifier();
IdentifierType identifierType = memberOrClassId.getType();
System.out.printf("IdentifierType = %s%n", identifierType);
switch (identifierType) {
case BASE58:
if (!isValidBase58(identifier)) {
throw new IllegalArgumentException("Invalid memberOrClassId. Must be a 44-character Base58 string.");
}
break;
case HEX:
if (identifier.length() != 7 || !identifier.matches("[0-9A-Fa-f]+")) {
throw new IllegalArgumentException("Invalid memberOrClassId. Must be a 7-character hexadecimal value.");
}
break;
default:
throw new IllegalArgumentException("Unsupported IdentifierType.");
}
// Generate the prefix for the ID
byte[] prefix = generateIdPrefix(idType, networkId, memberOrClassId, databaseOid, schemaOid, tableOid);
// Generate the concatenated hashes based on the prefix
IdTypeBitLengths bitLengths = getBitLengthsForIdType(idType);
if (bitLengths == null) {
throw new IllegalArgumentException("Unknown or unsupported idType.");
}
TreeMap hashResult = generateConcatenatedHashesWithPrefix(prefix, bitLengths, databaseOid, schemaOid, tableOid, recordOid);
// Combine the hash sections into a single ID
StringBuilder fullId = new StringBuilder();
hashResult.forEach((index, value) -> fullId.append("-").append(value));
// Store the ID in etcd
storeIdInEtcd(idType, fullId.toString());
System.out.println("Successfully created new Id!");
return fullId.toString();
}
public static String createNewIdNonDb(IdType idType, bftsmart.demo.itotcca.Cluster networkId2, MemberOrClassId memberOrClassId)
throws NoSuchAlgorithmException, InterruptedException, ExecutionException, TimeoutException {
System.out.println("We got this far - beginning to create new Id");
if (networkId2 == null || memberOrClassId == null) {
throw new IllegalArgumentException("Network ID and Member/Class ID cannot be null.");
}
String identifier = memberOrClassId.getIdentifier();
IdentifierType identifierType = memberOrClassId.getType();
System.out.printf("IdentifierType = %s%n", identifierType);
System.out.printf("💡 createNewIdNonDb: identifier = %s, length = %d%n",
memberOrClassId.getIdentifier(),
memberOrClassId.getIdentifier().length()
);
switch (identifierType) {
case BASE58:
if (!isValidBase58(identifier)) {
throw new IllegalArgumentException("Invalid memberOrClassId. Must be a 44-character Base58 string.");
}
break;
case HEX:
if (identifier.length() != 7 || !identifier.matches("[0-9A-Fa-f]+")) {
throw new IllegalArgumentException("Invalid memberOrClassId. Must be a 7-character hexadecimal value.");
}
break;
default:
throw new IllegalArgumentException("Unsupported IdentifierType.");
}
// ✅ Generate a **random** Id using the correct method
Id randomId = Id.random(idType, networkId2, memberOrClassId);
if (randomId == null) {
throw new RuntimeException("Failed to generate an ID. randomId is null.");
}
// ✅ Convert the Id to a **hex string**
String randomIdHex = randomId.toHexString();
if (randomIdHex == null || randomIdHex.isEmpty()) {
throw new RuntimeException("Generated ID is null or empty.");
}
System.out.println("Successfully created new random Id: " + randomIdHex);
// ✅ Store the ID in etcd
String key = storeIdInEtcd(idType, randomIdHex);
if (key == null) {
throw new IllegalStateException("Failed to store ID in etcd: key is null.");
}
System.out.println("Successfully stored ID in etcd, with key: " + key);
return randomIdHex;
}
// Store ID in etcd
public static String storeIdInEtcd(IdType idType, String id) throws InterruptedException, ExecutionException, TimeoutException {
KV kvClient = etcdClient.getKVClient();
// 🔹 Generate the etcd key using a switch expression
String key = switch (idType) {
case PEER_NODE_SUPERNODE -> "/config/ids/peer/supernode/" + getContainerName();
case PEER_NODE_SECONDARY_NODE -> "/config/ids/peer/secondary/" + getContainerName();
//case PEER_NODE_TERTIARY_NODE -> "/config/ids/peer/tertiary/" + getDeviceUniqueId();
case DATABASE -> "/config/ids/database/" + getDatabaseKey(id);
case SCHEMA -> "/config/ids/schema/" + getSchemaKey(id);
case TABLE_CLASS_A, TABLE_BFT_CLASS_UR -> "/config/ids/table/" + getTableKey(id);
case RECORD_CLASS_A, RECORD_BFT_CLASS_UR, RECORD_DHT_CLASS_UR -> "/config/ids/record/" + getRecordKey(id);
case FULL_CLASS_A_KEY -> "/config/ids/full/classA/" + getFullClassAKey(id);
case FULL_BFT_CLASS_UR_KEY -> "/config/ids/full/bftclassUR/" + getFullBFTClassURKey(id);
case FULL_DHT_CLASS_UR_KEY -> "/config/ids/full/dhtclassUR/" + getFullDHTClassURKey(id);
case FULL_BFT_CLASS_U_KEY -> "/config/ids/full/classU/" + getFullBFTClassUKey(id);
case FULL_DHT_TX_BLDER_META -> "/config/ids/dht/blder/" + getDhtTxBlderMetaKey(id);
case DHT_VALUE, ROLE, CLIENT_PERSONAL -> "/config/ids/" + idType.name().toLowerCase() + "/" + id;
default -> "/config/ids/unknown";
};
// Convert key and value to ByteSequence
ByteSequence keyByteSequence = ByteSequence.from(key, StandardCharsets.UTF_8);
ByteSequence valueByteSequence = ByteSequence.from(id, StandardCharsets.UTF_8);
boolean registered = false;
int retries = 10;
while (!registered && retries > 0) {
try {
kvClient.put(keyByteSequence, valueByteSequence).get(); // Store value in etcd
// Verify storage
CompletableFuture getFuture = kvClient.get(keyByteSequence);
GetResponse getResponse = getFuture.get();
registered = !getResponse.getKvs().isEmpty();
if (registered) {
System.out.printf("✅ ID successfully stored in etcd with key = %s%n", key);
return key;
}
} catch (Exception e) {
System.err.printf("❌ Failed to register ID (attempt %d/10), retrying...%n", (11 - retries));
e.printStackTrace();
}
retries--;
Thread.sleep(5000); // Ensure sleep is not interrupted
}
throw new IllegalStateException("❌ Failed to register ID after 10 attempts.");
}
public static String storeValueInEtcd(Integer entnum, Long slabId, byte[] valueByteArray) throws InterruptedException {
if (etcdClient == null) {
etcdClient = initializeEtcdClient();
}
KV kvClient = etcdClient.getKVClient();
UUID txId = UUID.randomUUID();
// 🔹 Generate the etcd key dynamically
String key = String.format("/metadata/values/txId/%s/networkId/%s/entnum/%d/databaseId/%s/schemaId/%s/tableId/%s/slabId/%d/blockId/%s",
txId, networkId, entnum, databaseId, schemaId, tableId, slabId, blockId);
// Convert key and value to ByteSequence
ByteSequence keyByteSequence = ByteSequence.from(key, StandardCharsets.UTF_8);
ByteSequence valueByteSequence = ByteSequence.from(valueByteArray); // ✅ Corrected
boolean registered = false;
int retries = 10;
while (!registered && retries > 0) {
try {
// 🔹 Store value in etcd
kvClient.put(keyByteSequence, valueByteSequence).get();
// 🔹 Verify storage
CompletableFuture getFuture = kvClient.get(keyByteSequence);
GetResponse getResponse = getFuture.get();
registered = !getResponse.getKvs().isEmpty();
if (registered) {
System.out.printf("✅ Value successfully stored in etcd with key = %s%n", key);
return key;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // ✅ Preserve thread interruption state
throw new IllegalStateException("❌ Interrupted while registering value in etcd.", e);
} catch (Exception e) {
System.err.printf("❌ Failed to register value (attempt %d/10), retrying...%n", (11 - retries));
e.printStackTrace();
}
retries--;
Thread.sleep(5000); // ✅ Ensure sleep is not interrupted
}
throw new IllegalStateException("❌ Failed to register value in etcd after 10 attempts.");
}
public static String getEtcdKey(IdType idType, String id) throws InterruptedException, ExecutionException, TimeoutException {
// 🔹 Generate the etcd key using a switch expression
String key = switch (idType) {
case PEER_NODE_SUPERNODE -> "/config/ids/peer/supernode/" + getContainerName();
case PEER_NODE_SECONDARY_NODE -> "/config/ids/peer/secondary/" + getContainerName();
//case PEER_NODE_TERTIARY_NODE -> "/config/ids/peer/tertiary/" + getDeviceUniqueId();
case DATABASE -> "/config/ids/database/" + getDatabaseKey(id);
case SCHEMA -> "/config/ids/schema/" + getSchemaKey(id);
case TABLE_CLASS_A -> "/config/ids/table/classA/" + getTableKey(id);
case TABLE_BFT_CLASS_UR -> "/config/ids/table/classUR/" + getTableKey(id);
case DHT_TX_BLDER_TABLE -> "/config/ids/table/dhtclassU/" + getTableKey(id);
case TABLE_BFT_CLASS_U -> "/config/ids/table/bftclassU/" + getTableKey(id);
case RECORD_CLASS_A -> "/config/ids/record/classA/" + getRecordKey(id);
case RECORD_BFT_CLASS_UR -> "/config/ids/record/classUR/" + getRecordKey(id);
case FULL_CLASS_A_KEY -> "/config/ids/full/classA/" + getFullClassAKey(id);
case FULL_BFT_CLASS_UR_KEY -> "/config/ids/full/bftclassUR/" + getFullBFTClassURKey(id);
case FULL_DHT_CLASS_UR_KEY -> "/config/ids/full/dhtclassUR/" + getFullDHTClassURKey(id);
case FULL_BFT_CLASS_U_KEY -> "/config/ids/full/classU/" + getFullBFTClassUKey(id);
case FULL_DHT_TX_BLDER_META -> "/config/ids/dht/blder/" + getDhtTxBlderMetaKey(id);
case ROLE, CLIENT_PERSONAL -> "/config/ids/" + idType.name().toLowerCase() + "/" + id;
case DHT_VALUE -> String.format("/metadata/values/txId/%s/networkId/%s/entnum/%d/databaseId/%s/schemaId/%s/tableId/%s/slabId/%d/blockId/%s",
txId, networkId, entnum, databaseId, schemaId, tableId, slabId, blockId);
case NETWORK_ID -> "/config/ids/network/" + getNetworkName();
case MEMBER_OR_CLASS_ID -> "/config/ids/class/" + getDatabaseName();
default -> "/config/ids/unknown";
};
return key;
}
private static String getDatabaseName() {
String dbName = System.getenv("DATABASE_NAME");
return dbName;
}
public static String getNetworkName() {
String clusterEnv = System.getenv("CLUSTER");
if (clusterEnv == null) {
throw new IllegalStateException("CLUSTER environment variable is not set.");
}
String baseName = clusterEnv.startsWith("SUPER")
? clusterEnv.substring("SUPER".length())
: clusterEnv;
return baseName.toLowerCase();
}
public static String getContainerName() {
if (getClusterNameLowerCase().contains("super")) {
String clusterNameWithoutSuper = getClusterNameLowerCase().replaceFirst("super", "");
containerName = "bft-dht-" + clusterNameWithoutSuper + "-super";
} else {
containerName = "bft-dht-" + getClusterNameLowerCase() + "-" + System.getenv("NODE_ID");
}
return containerName;
}
public static bftsmart.demo.itotcca.Cluster extractClusterFromId(Id id) {
String idHex = id.toHex();
String clusterPrefix = idHex.substring(0, 7); // Adjust bits/length as appropriate
return bftsmart.demo.itotcca.Cluster.fromId7(clusterPrefix);
}
// Dummy mappings for database, schema, table, and record names
private static final Map databaseMap = new TreeMap<>();
private static final Map schemaMap = new TreeMap<>();
private static final Map tableMap = new TreeMap<>();
static {
// Populate dummy mappings
databaseMap.put("1a2b3c", "EnterpriseDB");
schemaMap.put("4d5e6f", "FinanceSchema");
tableMap.put("7g8h9i", "TransactionsTable");
}
public static String[] resolveKeyToTableAndRecord(String fullDHTKey) {
// Remove the 8-bit prefix
String baseKey = fullDHTKey.substring(8);
// Extracting different parts of the key based on bit allocations
String networkId = baseKey.substring(0, 28);
String memberOrClassId = baseKey.substring(28, 56);
String databaseId = baseKey.substring(56, 154);
String schemaId = baseKey.substring(154, 254);
String tableId = baseKey.substring(254, 354);
String recordId = baseKey.substring(354, 504); // Keep for direct use
// Fetch names from etcd
String databaseName = getDatabaseFromId("/config/mappings/database/" + databaseId);
String schemaName = getSchemaFromId("/config/mappings/schema/" + schemaId);
String tableName = getTableFromId("/config/mappings/table/" + tableId);
if (databaseName == null || schemaName == null || tableName == null) {
System.err.println("❌ Missing mapping for key: " + fullDHTKey);
return null;
}
// Return human-readable values for processing
return new String[]{databaseName, schemaName, tableName, recordId};
}
public static String resolveTableAndRecordToKey(String fullTableNamePlusRecordKey) {
// Remove the 8-bit prefix
String mappedNameAt = fullTableNamePlusRecordKey;
// Extracting different parts of the key based on bit allocations
String networkId = mappedNameAt.substring(0, 28);
String memberOrClassId = mappedNameAt.substring(28, 56);
String databaseName = mappedNameAt.substring(56, 154);
String schemaName = mappedNameAt.substring(154, 254);
String tableName = mappedNameAt.substring(254, 354);
String recordId = mappedNameAt.substring(354, 504); // Keep for direct use
// Fetch names from etcd
String databaseId = getIdFromDatabaseName("/config/mappings/databaseNames/" + databaseName);
String schemaId = getIdFromSchemaName("/config/mappings/schemaNames/" + schemaName);
String tableId = getIdFromTableName("/config/mappings/tableNames/" + tableName);
if (databaseId == null || schemaId == null || tableId == null) {
System.err.println("❌ Missing mapping for key: " + fullTableNamePlusRecordKey);
return null;
}
// Return human-readable values for processing
return "0F" + networkId + memberOrClassId + databaseId + schemaId + tableId + recordId;
}
public static void storeIdNameMappings(String databaseId, String databaseName,
String schemaId, String schemaName,
String tableId, String tableName) {
// Store ID -> Name mappings
putToEtcd("/config/mappings/database/" + databaseId, databaseName.getBytes(StandardCharsets.UTF_8));
putToEtcd("/config/mappings/schema/" + schemaId, schemaName.getBytes(StandardCharsets.UTF_8));
putToEtcd("/config/mappings/table/" + tableId, tableName.getBytes(StandardCharsets.UTF_8));
// Store Name -> ID mappings (for reverse lookup)
putToEtcd("/config/mappings/databaseNames/" + databaseName, databaseId.getBytes(StandardCharsets.UTF_8));
putToEtcd("/config/mappings/schemaNames/" + schemaName, schemaId.getBytes(StandardCharsets.UTF_8));
putToEtcd("/config/mappings/tableNames/" + tableName, tableId.getBytes(StandardCharsets.UTF_8));
}
public static String getDatabaseFromId(String databaseId) {
String lookupKey = "/config/mappings/database/" + databaseId;
return fetchEtcdMapping(lookupKey);
}
private static String getSchemaFromId(String schemaId) {
String lookupKey = "/config/mappings/schema/" + schemaId;
return fetchEtcdMapping(lookupKey);
}
private static String getTableFromId(String tableId) {
String lookupKey = "/config/mappings/table/" + tableId;
return fetchEtcdMapping(lookupKey);
}
// 🔹 Fetch mapping from etcd
private static String fetchEtcdMapping(String key) {
byte[] resultBytes = getFromEtcd(key);
return (resultBytes != null) ? new String(resultBytes, StandardCharsets.UTF_8) : "UNKNOWN";
}
public static String getIdFromDatabaseName(String databaseName) {
String lookupKey = "/config/mappings/databaseNames/" + databaseName;
return fetchEtcdMapping(lookupKey);
}
private static String getIdFromSchemaName(String schemaName) {
String lookupKey = "/config/mappings/schemaNames/" + schemaName;
return fetchEtcdMapping(lookupKey);
}
private static String getIdFromTableName(String tableName) {
String lookupKey = "/config/mappings/tableNames/" + tableName;
return fetchEtcdMapping(lookupKey);
}
private static final ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50061)
.usePlaintext()
.build();
private static final KeyLookupServiceGrpc.KeyLookupServiceBlockingStub keyLookupStub =
KeyLookupServiceGrpc.newBlockingStub(channel);
// 🔹 Replace the direct method calls with gRPC requests to key-lookup-service
public static String getDatabaseKey(String fullId) {
KeyLookupRequest request = KeyLookupRequest.newBuilder()
.setFullId(fullId)
.build();
KeyLookupResponse response = keyLookupStub.lookupDatabaseKey(request);
return response.getDatabaseKey();
}
public static String getSchemaKey(String fullId) {
KeyLookupRequest request = KeyLookupRequest.newBuilder()
.setFullId(fullId)
.build();
KeyLookupResponse response = keyLookupStub.lookupSchemaKey(request);
return response.getSchemaKey();
}
public static String getTableKey(String fullId) {
KeyLookupRequest request = KeyLookupRequest.newBuilder()
.setFullId(fullId)
.build();
KeyLookupResponse response = keyLookupStub.lookupTableKey(request);
return response.getTableKey();
}
public static String getRecordKey(String fullId) {
KeyLookupRequest request = KeyLookupRequest.newBuilder()
.setFullId(fullId)
.build();
KeyLookupResponse response = keyLookupStub.lookupRecordKey(request);
return response.getRecordKey();
}
public static String getFullClassAKey(String fullId) {
return replaceKeyType(fullId, "00010001"); // 110 for Full-Class Keys
}
public static String getFullBFTClassURKey(String fullId) {
return replaceKeyType(fullId, "00010100");
}
public static String getFullDHTClassURKey(String fullId) {
return replaceKeyType(fullId, "00010101");
}
public static String getFullBFTClassUKey(String fullId) {
return replaceKeyType(fullId, "00010010");
}
public static String getDhtTxBlderMetaKey(String fullId) {
return replaceKeyType(fullId, "00010011");
}
public static String getDhtTxTraceMetaKey(String fullId) {
return replaceKeyType(fullId, "00010101");
}
private static String replaceKeyType(String fullId, String newKeyType) {
return newKeyType + fullId.substring(8); // Replace first 8 bits only
}
// Generate a unique client ID based on UUID or existing DHT ID
private static String getClientUniqueId() {
return UUID.randomUUID().toString(); // Alternatively, fetch from DHT
}
/** Generate a unique device ID based on UUID or existing DHT ID
public static String getDeviceUniqueId() throws InterruptedException, ExecutionException, TimeoutException {
if (getDeviceUniqueId() == null) {
return UUID.randomUUID().toString(); // Alternatively, fetch from DHT
} else {
String deviceUniqueId = IdManager.getDeviceUniqueId();
String keyPath = "/config/configs/peer/tertiary/" + IdManager.getDeviceUniqueId();
Client etcdClient = initializeEtcdClient();
ByteSequence key = ByteSequence.from(keyPath, StandardCharsets.UTF_8);
CompletableFuture future = etcdClient.getKVClient().get(key);
GetResponse response = future.get(3, TimeUnit.SECONDS);
if (response.getKvs().isEmpty()) {
throw new IllegalStateException("❌ No config found for key: " + keyPath);
}
String configData = response.getKvs().get(0).getValue().toString(StandardCharsets.UTF_8);
// Parse configData to create DefaultConfiguration if you serialize JSON
throw new UnsupportedOperationException("Implement JSON deserialization as needed.");
}
}**/
public static TreeMap generateHashes(IdType idType, String networkId, MemberOrClassId memberOrClassId) throws NoSuchAlgorithmException {
// Validate inputs
if (networkId.length() != 7 || !networkId.matches("[0-9A-Fa-f]+")) {
throw new IllegalArgumentException("Invalid networkId. Must be a 7-character hexadecimal value.");
}
boolean isBase58 = networkId.equals("0000000") && idType == IdType.CLIENT_PERSONAL;
if (isBase58) {
if (!isValidBase58(memberOrClassId.toString())) {
throw new IllegalArgumentException("Invalid memberOrClassId. Must be a 44-character Base58 string.");
}
} else {
if (memberOrClassId.toString().length() != 7 || !memberOrClassId.toString().matches("[0-9A-Fa-f]+")) {
throw new IllegalArgumentException("Invalid memberOrClassId. Must be a 7-character hexadecimal value.");
}
}
// Generate the prefix
byte[] prefix = generateIdPrefix(idType, networkId, memberOrClassId, databaseOid, schemaOid, tableOid);
// Get bit lengths for the ID type
IdTypeBitLengths bitLengths = getBitLengthsForIdType(idType);
if (bitLengths == null) {
throw new IllegalArgumentException("Unknown or unsupported idType.");
}
// Generate and return hashes
return generateConcatenatedHashesWithPrefix(prefix, bitLengths, databaseOid, schemaOid, tableOid, recordOid);
}
public static class IdTypeBitLengths {
int[] bitLengths;
boolean forcePublicSchema;
public IdTypeBitLengths(int[] bitLengths, boolean forcePublicSchema) {
this.bitLengths = bitLengths;
this.forcePublicSchema = forcePublicSchema;
}
}
private static IdTypeBitLengths getBitLengthsForIdType(IdType idType) {
boolean forcePublicSchema = (idType == IdType.FULL_DHT_TX_BLDER_META); // ✅ Dynamic schema decision
return switch (idType) {
case PEER_NODE_SUPERNODE, PEER_NODE_SECONDARY_NODE, PEER_NODE_TERTIARY_NODE, ROLE, DHT_VALUE ->
new IdTypeBitLengths(new int[]{64, 448}, false);
case DATABASE ->
new IdTypeBitLengths(new int[]{64, 98}, false);
case SCHEMA ->
new IdTypeBitLengths(new int[]{64, 100}, forcePublicSchema); // ✅ Now dynamic!
case TABLE_CLASS_A, TABLE_BFT_CLASS_UR, TABLE_BFT_CLASS_U, TABLE_DHT_CLASS_UR ->
new IdTypeBitLengths(new int[]{64, 100}, false);
case FULL_BFT_CLASS_U_KEY ->
new IdTypeBitLengths(new int[]{64, 98, 100, 100}, false);
case RECORD_CLASS_A, RECORD_BFT_CLASS_UR, RECORD_DHT_CLASS_UR ->
new IdTypeBitLengths(new int[]{64, 150}, false);
case FULL_CLASS_A_KEY, FULL_BFT_CLASS_UR_KEY, FULL_DHT_CLASS_UR_KEY ->
new IdTypeBitLengths(new int[]{64, 98, 100, 100, 150}, false);
case FULL_DHT_TX_BLDER_META ->
new IdTypeBitLengths(new int[]{64, 98, 100, 100}, true); // ✅ Always public
case DHT_TX_BLDER_RECORD ->
new IdTypeBitLengths(new int[]{64, 100}, false); // ✅ No schema concern → false
case CLIENT_PERSONAL ->
new IdTypeBitLengths(new int[]{64, 448}, false);
case NETWORK_ID ->
new IdTypeBitLengths(new int[]{8, 28}, false);
case MEMBER_OR_CLASS_ID ->
new IdTypeBitLengths(new int[]{8, 28}, false);
default -> null;
};
}
private static byte[] generateNonDbIdPrefix(IdType idType, bftsmart.demo.itotcca.Cluster networkId2, MemberOrClassId memberOrClassId) {
try {
MessageDigest sha3 = MessageDigest.getInstance("SHA3-512");
// Generate a unique identifier based on network ID and node ID
String prefixInput = idType.getHexPrefix() + "-" + networkId2 + "-" + memberOrClassId.getIdentifier();
byte[] hash = sha3.digest(prefixInput.getBytes(StandardCharsets.UTF_8));
return Arrays.copyOf(hash, 8); // Take first 8 bytes for the prefix (64 bits)
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA3-512 algorithm not available.", e);
}
}
private static byte[] generateIdPrefix(IdType idType, String networkId, MemberOrClassId memberOrClassId,
String databaseOid, String schemaOid, String tableOid) {
try {
MessageDigest sha3 = MessageDigest.getInstance("SHA3-512");
// Ensure all OIDs are properly formatted (pad with leading zeros)
databaseOid = String.format("%7s", databaseOid).replace(' ', '0');
schemaOid = String.format("%7s", schemaOid).replace(' ', '0');
tableOid = String.format("%7s", tableOid).replace(' ', '0');
// Concatenate components for hashing
String input = idType.getHexPrefix() + "-" + networkId + "-" + memberOrClassId.getIdentifier() +
"-" + databaseOid + "-" + schemaOid + "-" + tableOid;
// Compute SHA3-512 hash
byte[] hash = sha3.digest(input.getBytes(StandardCharsets.UTF_8));
// Extract the first 8 bytes (64-bit prefix)
return Arrays.copyOf(hash, 8);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA3-512 algorithm not available.", e);
}
}
private static TreeMap generateConcatenatedHashesWithPrefix(
byte[] prefix, IdTypeBitLengths bitLengthsInfo,
String databaseOid, String schemaOid, String tableOid, String recordOid) throws NoSuchAlgorithmException {
// Convert prefix from byte[] to Hex
String prefixHex = bytesToHex(prefix);
// Ensure OIDs are not null to prevent NullPointerException
databaseOid = (databaseOid != null) ? databaseOid : "";
schemaOid = (schemaOid != null) ? schemaOid : "";
tableOid = (tableOid != null) ? tableOid : "";
recordOid = (recordOid != null) ? recordOid : "";
// Concatenate prefix + OIDs for hashing
String fullInput = prefixHex + "-" + databaseOid + "-" + schemaOid + "-" + tableOid + "-" + recordOid;
// Create SHA3-512 hash
MessageDigest digest = MessageDigest.getInstance("SHA3-512");
byte[] hash = digest.digest(fullInput.getBytes(StandardCharsets.UTF_8));
// Extract bit lengths array
int[] bitLengths = bitLengthsInfo.bitLengths;
// Create map for hash sections
TreeMap hashSections = new TreeMap<>();
int byteIndex = 0;
for (int bitLength : bitLengths) {
int byteLength = bitLength / 8;
// Ensure we do not exceed the hash length
if (byteIndex + byteLength > hash.length) {
break; // Avoid `ArrayIndexOutOfBoundsException`
}
// Extract and store hash segment
String segment = bytesToHex(Arrays.copyOfRange(hash, byteIndex, byteIndex + byteLength));
hashSections.put(byteIndex, segment);
byteIndex += byteLength;
}
return hashSections;
}
private static String bytesToHex(byte[] bytes) {
BigInteger bigInteger = new BigInteger(1, bytes);
StringBuilder hexString = new StringBuilder(bigInteger.toString(16));
while (hexString.length() < bytes.length * 2) {
hexString.insert(0, '0');
}
return hexString.toString();
}
private static boolean isValidBase58(String value) {
final String BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
for (char c : value.toCharArray()) {
if (BASE58_ALPHABET.indexOf(c) == -1) {
return false;
}
}
return value.length() == 44;
}
// Helper: Convert bytes to binary string
public static String bytesToBinaryString(byte[] bytes) {
StringBuilder binary = new StringBuilder();
for (byte b : bytes) {
binary.append(String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0'));
}
return binary.toString();
}
public static Id createHashedId(String input, IdType type) {
return new Id(Id.hashValue(input), type);
}
public static byte[] uuidToByteArray(UUID uuid) {
ByteBuffer buffer = ByteBuffer.allocate(16);
buffer.putLong(uuid.getMostSignificantBits());
buffer.putLong(uuid.getLeastSignificantBits());
return buffer.array();
}
public static boolean isValidId(Id id, IdType expectedType) {
return id != null && id.getType() == expectedType;
}
public static Id computeDistance(Id id1, Id id2) {
byte[] resultBytes = new byte[Id.SIZE];
byte[] bytes1 = id1.getBytes();
byte[] bytes2 = id2.getBytes();
for (int i = 0; i < Id.SIZE; i++) {
resultBytes[i] = (byte) (bytes1[i] ^ bytes2[i]);
}
return new Id(resultBytes, id1.getType());
}
public static Id of(Prefix prefix) {
if (prefix == null) {
throw new IllegalArgumentException("Prefix cannot be null.");
}
// Create a new Id using the bytes from the Prefix and its depth
byte[] prefixBytes = prefix.getBytes(); // Assuming Prefix has a getBytes() method
int depth = prefix.getDepth(); // Assuming Prefix has a getDepth() method
// Copy the bytes from the prefix and adjust the remaining bits based on depth
byte[] idBytes = new byte[Id.SIZE];
System.arraycopy(prefixBytes, 0, idBytes, 0, prefixBytes.length);
// Set remaining bits to 0 beyond the depth
int byteIndex = depth / 8;
int bitIndex = depth % 8;
if (byteIndex < idBytes.length) {
idBytes[byteIndex] &= ~(0xFF >> bitIndex); // Zero out bits beyond the depth
}
for (int i = byteIndex + 1; i < idBytes.length; i++) {
idBytes[i] = 0; // Zero out all bytes beyond the depth
}
return new bftsmart.demo.itotcca.IdManager.Id(idBytes, IdType.DHT_VALUE);
}
public static IdManager.Id fromId(Id id) {
// Conversion logic from Id to IdManager.Id
return new IdManager.Id(id.getBytes(), IdType.DHT_VALUE); // Example
}
public static byte[] hashValue(String nodeSeed) {
try {
// Use SHA3-512 for strong hashing
MessageDigest digest = MessageDigest.getInstance("SHA3-512");
return digest.digest(nodeSeed.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA3-512 algorithm not available.", e);
}
}
public static bftsmart.demo.itotcca.Cluster getNetworkIdFromEnv() {
System.out.println("[DEBUG] Raw networkName = " + getNetworkName());
String networkName = getNetworkName();
if (networkName == null || networkName.isEmpty()) {
throw new IllegalStateException("CLUSTER environment variable is not set.");
}
try {
// Hash using SHA-256
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(networkName.getBytes(StandardCharsets.UTF_8));
// Convert first 4 bytes to hex string (8 hex chars), then truncate to 7
StringBuilder hexString = new StringBuilder();
for (int i = 0; i < 4; i++) {
String hex = Integer.toHexString(0xff & hash[i]);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
String id7 = hexString.toString().substring(0, 7).toUpperCase();
// Match the 7-character hex ID to an enum's identifier field
for (bftsmart.demo.itotcca.Cluster id : bftsmart.demo.itotcca.Cluster.values()) {
if (id.getId7().equalsIgnoreCase(id7)) {
return id;
}
}
throw new IllegalStateException("No matching Cluster found for: " + id7);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA-256 not available", e);
}
}
public static MemberOrClassId getMemberOrClassIdFromEnv() {
String dbName = System.getenv("DATABASE_NAME");
if (dbName == null || dbName.isEmpty()) {
throw new IllegalStateException("DATABASE_NAME environment variable is not set.");
}
try {
return MemberOrClassId.parse(dbName); // 👈 Use the custom method
} catch (IllegalArgumentException e) {
throw new IllegalStateException("No matching MemberOrClassId for: " + dbName);
}
}
public static bftsmart.demo.itotcca.IdManager.Id createIdFromString(String key) {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("Key cannot be null or empty.");
}
try {
// Generate a hashed ID from the key using SHA-256
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hashBytes = digest.digest(key.getBytes(StandardCharsets.UTF_8));
// Truncate or pad the hash to match the required ID length
byte[] idBytes = Arrays.copyOf(hashBytes, Id.ID_BYTE_LENGTH);
return new bftsmart.demo.itotcca.IdManager.Id(idBytes, IdType.DHT_VALUE);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("SHA-256 algorithm not found.", e);
}
}
/**
* Generates a **512-bit ID (FULL_CLASS_A_KEY)** for Database + Schema + Table + Record.
*/
public static Id generateFullClassAKey(String networkId, String databaseOid, String schemaOid, String tableOid, String recordOid) throws NoSuchAlgorithmException {
String prefix = IdType.FULL_CLASS_A_KEY.getHexPrefix();
return generateFullKey(prefix, networkId, databaseOid, schemaOid, tableOid, recordOid);
}
/**
* Generates a **5122-bit ID (FULL_CLASS_UR_KEY)** for Database + Schema + Table + Record.
*/
public static Id generateFullBFTClassURKey(String networkId, String databaseOid, String schemaOid, String tableOid, String recordOid) throws NoSuchAlgorithmException {
String prefix = IdType.FULL_BFT_CLASS_UR_KEY.getHexPrefix();
return generateFullKey(prefix, networkId, databaseOid, schemaOid, tableOid, recordOid);
}
/**
* Generates a **512-bit ID (FULL_CLASS_UR_KEY)** for Database + Schema + Table + Record.
*/
public static Id generateFullDHTClassURKey(String networkId, String databaseOid, String schemaOid, String tableOid, String recordOid) throws NoSuchAlgorithmException {
String prefix = IdType.FULL_DHT_CLASS_UR_KEY.getHexPrefix();
return generateFullKey(prefix, networkId, databaseOid, schemaOid, tableOid, recordOid);
}
/**
* Generates a **362-bit ID (FULL_CLASS_U_KEY)** for Database + Schema + Table.
*/
public static Id generateFullBFTClassUKey(String networkId, String databaseOid, String schemaOid, String tableOid) throws NoSuchAlgorithmException {
String prefix = IdType.FULL_BFT_CLASS_U_KEY.getHexPrefix();
return generateFullKey(prefix, networkId, databaseOid, schemaOid, tableOid, null);
}
/**
* Generates a **362-bit ID (FULL_CLASS_U_KEY)** for Database + Schema + Table.
*/
public static Id generateFullDHTClassUKey(String networkId, String databaseOid, String schemaOid, String tableOid) throws NoSuchAlgorithmException {
String prefix = IdType.FULL_DHT_TX_BLDER_META.getHexPrefix();
return generateFullKey(prefix, networkId, databaseOid, schemaOid, tableOid, null);
}
/**
* Generalized Key Generator with Prefix.
*/
private static Id generateFullKey(String prefix, String networkId, String databaseOid, String schemaOid, String tableOid, String recordOid) throws NoSuchAlgorithmException {
MessageDigest sha3 = MessageDigest.getInstance("SHA3-512");
StringBuilder fullKey = new StringBuilder()
.append(networkId)
.append(databaseOid)
.append(schemaOid)
.append(tableOid);
if (recordOid != null) {
fullKey.append(recordOid);
}
byte[] hashedId = sha3.digest(fullKey.toString().getBytes(StandardCharsets.UTF_8));
return new Id(Arrays.copyOf(hashedId, 64), IdType.DHT_VALUE);
}
/**
* Enum for ID types with reorganized prefixes.
*/
public enum IdType {
PEER_NODE_SUPERNODE("00"),
PEER_NODE_SECONDARY_NODE("01"),
PEER_NODE_TERTIARY_NODE("02"),
DATABASE("03"),
SCHEMA("04"),
TABLE_CLASS_A("05"),
RECORD_CLASS_A("06"),
TABLE_BFT_CLASS_U("07"),
TABLE_BFT_CLASS_UR("08"),
RECORD_BFT_CLASS_UR("09"),
TABLE_DHT_CLASS_UR("0A"),
RECORD_DHT_CLASS_UR("0B"),
DHT_TX_BLDER_TABLE("0C"),
DHT_TX_BLDER_RECORD("0D"),
ROLE("0E"),
CLIENT_PERSONAL("0F"),
DHT_VALUE("10"),
FULL_CLASS_A_KEY("11"), // 512-bit Key (DB + Schema + Table + Record)
FULL_BFT_CLASS_U_KEY("12"), // 362-bit Key (DB + Schema + Table)
FULL_DHT_TX_BLDER_META("13"),
FULL_BFT_CLASS_UR_KEY("14"), // 512-bit Key (DB + Schema + Table + Record)
FULL_DHT_CLASS_UR_KEY("15"), // 512-bit Key (DB + Schema + Table + Record)
NETWORK_ID("16"),
MEMBER_OR_CLASS_ID("17");
private final String hexPrefix;
IdType(String hexPrefix) {
this.hexPrefix = hexPrefix;
}
String getHexPrefix() {
return this.hexPrefix;
}
// Optional: Get prefix as a single byte if needed
public byte getPrefixByte() {
return (byte) Integer.parseInt(hexPrefix, 16);
}
@Override
public String toString() {
return this.name() + "(" + hexPrefix + ")";
}
// Method to generate a new Full Class_A ID and return the result
public static String createNewFullAId(IdType FULL_CLASS_A_KEY, String networkId, MemberOrClassId memberOrClassId, GeneralMessage request) throws Exception {
// Extract database, schema, and table name from request
String fullTableName = request.getFullTableName(); // Expected format: "database.schema.table"
// Split the full table name to get individual components
String[] parts = fullTableName.split("\\.");
if (parts.length != 3 && parts.length != 4) {
throw new IllegalArgumentException("Invalid FullTableName format. Expected format: database.schema.table");
}
String database = parts[0];
String schemaName = parts[1];
String tableName = parts[2];
String recordId = request.getRecordId();
// Retrieve OIDs using the getOIDs method
Map oidMap = getOIDs(networkId, database, schemaName, tableName);
if (!oidMap.containsKey("databaseOid") || !oidMap.containsKey("tableOid")) {
throw new RuntimeException("Failed to retrieve necessary OIDs for FullClassAKey generation.");
}
String databaseOid = Long.toHexString(oidMap.get("databaseOid"));
String schemaOid = Long.toHexString(oidMap.get("schemaOid")); // Use table OID as a substitute for schema
String tableOid = Long.toHexString(oidMap.get("tableOid")); // Table OID
if (recordId != null) {
Long.toHexString(oidMap.get("recordOid"));
}
// Validate inputs
if (networkId.length() != 7 || !networkId.matches("[0-9A-Fa-f]+")) {
throw new IllegalArgumentException("Invalid networkId. Must be a 7-character hexadecimal value.");
}
if (memberOrClassId.toString().length() != 7 || !memberOrClassId.toString().matches("[0-9A-Fa-f]+")) {
throw new IllegalArgumentException("Invalid memberOrClassId. Must be a 7-character hexadecimal value.");
}
// Generate the prefix for the ID (Modify to include OIDs)
byte[] prefix = generateIdPrefix(FULL_CLASS_A_KEY, networkId, memberOrClassId, databaseOid, schemaOid, tableOid);
// Generate the concatenated hashes based on the prefix (Modify to hash OIDs as well)
IdTypeBitLengths bitLengths = getBitLengthsForIdType(FULL_CLASS_A_KEY);
if (bitLengths == null) {
throw new IllegalArgumentException("Unknown or unsupported idType.");
}
TreeMap hashResult = generateConcatenatedHashesWithPrefix(prefix, bitLengths, databaseOid, schemaOid, tableOid, recordOid);
// Combine the hash sections into a single ID
StringBuilder fullId = new StringBuilder();
hashResult.forEach((index, value) -> fullId.append("-").append(value));
return fullId.toString();
}
// Method to generate a new Full Class_UR ID and return the result
public static String createNewFullURId(IdType FULL_CLASS_UR_KEY, String networkId, MemberOrClassId memberOrClassId2, GeneralMessage request) throws Exception {
// Validate inputs
if (networkId.length() != 7 || !networkId.matches("[0-9A-Fa-f]+")) {
throw new IllegalArgumentException("Invalid networkId. Must be a 7-character hexadecimal value.");
}
if (memberOrClassId2.toString().length() != 7 || !memberOrClassId2.toString().matches("[0-9A-Fa-f]+")) {
throw new IllegalArgumentException("Invalid memberOrClassId. Must be a 7-character hexadecimal value.");
}
// Extract database, schema, and table name from request
String fullTableName = request.getFullTableName();
String[] parts = fullTableName.split("\\.");
if (parts.length != 3) {
throw new IllegalArgumentException("Invalid FullTableName format. Expected format: database.schema.table");
}
String database = parts[0];
String schemaName = parts[1];
String tableName = parts[2];
request.getRecordId();
// Retrieve OIDs using the getOIDs method
Map oidMap = getOIDs(networkId, database, schemaName, tableName);
if (!oidMap.containsKey("databaseOid") || !oidMap.containsKey("tableOid")) {
throw new RuntimeException("Failed to retrieve necessary OIDs for FullClassAKey generation.");
}
String databaseOid = Long.toHexString(oidMap.get("databaseOid"));
String schemaOid = Long.toHexString(oidMap.get("schemaOid"));
String tableOid = Long.toHexString(oidMap.get("tableOid"));
String recordOid = Long.toHexString(oidMap.get("recordOid"));
// Generate the prefix for the ID
byte[] prefix = generateIdPrefix(FULL_CLASS_UR_KEY, networkId, memberOrClassId2, databaseOid, schemaOid, tableOid);
// Generate the concatenated hashes based on the prefix
IdTypeBitLengths bitLengths = getBitLengthsForIdType(FULL_CLASS_UR_KEY);
if (bitLengths == null) {
throw new IllegalArgumentException("Unknown or unsupported idType.");
}
TreeMap hashResult = generateConcatenatedHashesWithPrefix(prefix, bitLengths, databaseOid, schemaOid, tableOid, recordOid);
// Combine the hash sections into a single ID
StringBuilder fullId = new StringBuilder();
hashResult.forEach((index, value) -> fullId.append("-").append(value));
String fullClassURId = fullId.toString();
// Store the suffix (last part after the last '-')
String[] idParts = fullClassURId.split("-");
String fullClassURIdSuffix = idParts[idParts.length - 1];
// Store in Redis/etcd for later retrieval
String cacheKey = "DHT_TX_TRACE_SUFFIX:" + request.getFullTableName();
putToEtcd(cacheKey, fullClassURIdSuffix.getBytes(StandardCharsets.UTF_8));
return fullClassURId;
}
public static String createAndStoreFullUId(IdType fullClassUKey, String networkId, MemberOrClassId memberOrClassId, GeneralMessage request) throws Exception {
String fullClassUId = createNewFullUId(fullClassUKey, networkId, memberOrClassId, request);
// Store the generated FullClassUId in Redis/etcd so that getUseCaseMetadataId can retrieve it
String useCaseKey = "tableToUseCase:" + request.getFullTableName();
putToEtcd(useCaseKey, fullClassUId.getBytes(StandardCharsets.UTF_8));
return fullClassUId;
}
// Method to generate a new Full Class_U ID and return the result
public static String createNewFullUId(IdType fullClassUKey, String networkId, MemberOrClassId memberOrClassId, GeneralMessage request) throws Exception {
String memberOrClassId3 = memberOrClassId.toString();
// Validate inputs
if (networkId.length() != 7 || !networkId.matches("[0-9A-Fa-f]+")) {
throw new IllegalArgumentException("Invalid networkId. Must be a 7-character hexadecimal value.");
} else {
if (memberOrClassId3.length() != 7 || !memberOrClassId3.matches("[0-9A-Fa-f]+")) {
throw new IllegalArgumentException("Invalid memberOrClassId. Must be a 7-character hexadecimal value.");
}
}
// Split the full table name to get individual components
String[] parts = fullTableName.split("\\.");
if (parts.length != 3 && parts.length != 4) {
throw new IllegalArgumentException("Invalid FullTableName format. Expected format: database.schema.table");
}
String database = parts[0];
String schemaName = parts[1];
String tableName = parts[2];
// Retrieve OIDs using the getOIDs method
Map oidMap = getOIDs(networkId, database, schemaName, tableName);
if (!oidMap.containsKey("databaseOid") || !oidMap.containsKey("tableOid")) {
throw new RuntimeException("Failed to retrieve necessary OIDs for FullClassAKey generation.");
}
String databaseOid = Long.toHexString(oidMap.get("databaseOid")); // DHT Use Case DataBase OID
String schemaOid = Long.toHexString(2200L); // DHT Use Case DataSchema (public)
String tableOid = Long.toHexString(oidMap.get("schemaOid")); // DHT Use Case DataTable OID
String recordOid = Long.toHexString(oidMap.get("tableOid")); // DHT Use Case Data Record OID
// Validate inputs
if (networkId.length() != 7 || !networkId.matches("[0-9A-Fa-f]+")) {
throw new IllegalArgumentException("Invalid networkId. Must be a 7-character hexadecimal value.");
}
if (memberOrClassId3.length() != 7 || !memberOrClassId3.matches("[0-9A-Fa-f]+")) {
throw new IllegalArgumentException("Invalid memberOrClassId. Must be a 7-character hexadecimal value.");
}
// Generate the prefix for the ID
byte[] prefix = generateIdPrefix(idType, networkId, memberOrClassId, databaseOid, schemaOid, tableOid);
// Generate the concatenated hashes based on the prefix
IdTypeBitLengths bitLengths = getBitLengthsForIdType(idType);
if (bitLengths == null) {
throw new IllegalArgumentException("Unknown or unsupported idType.");
}
TreeMap hashResult = generateConcatenatedHashesWithPrefix(prefix, bitLengths, databaseOid, schemaOid, tableOid, recordOid);
// Combine the hash sections into a single ID
StringBuilder fullId = new StringBuilder();
hashResult.forEach((index, value) -> fullId.append("-").append(value));
return fullId.toString();
}
public static String getRecordId() {
return recordId;
}
public static void setRecordId(String recordId) {
IdManager.recordId = recordId;
}
public static byte[] decode(String nodeId) {
if (nodeId == null || nodeId.length() % 2 != 0) {
throw new IllegalArgumentException("Invalid hex string: " + nodeId);
}
int len = nodeId.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(nodeId.charAt(i), 16) << 4)
+ Character.digit(nodeId.charAt(i+1), 16));
}
return data;
}
private static final HexFormat HEX = HexFormat.of();
/** Resolves a QUIC endpoint by nodeId hex string (e.g. 0x00A123...) */
public static String resolveQuicEndpoint(String nodeIdHex) {
return "quic://" + nodeIdHex + ".quic-network.svc.cluster.local";
}
public static String createAndStoreFullUId(IdType type, Cluster networkId, MemberOrClassId memberOrClassId, GeneralMessage request) {
String data = networkId.getId7() + memberOrClassId.getIdentifier()
+ request.getFullTableName();
return hashWithPrefix(type, data);
}
public static String createNewFullURId(IdType type, Cluster networkId, MemberOrClassId memberOrClassId, GeneralMessage request) {
String data = networkId.getId7() + memberOrClassId.getIdentifier()
+ request.getFullTableName() + request.getRecordId();
return hashWithPrefix(type, data);
}
public static String createNewFullAId(IdType type, Cluster networkId, MemberOrClassId memberOrClassId, GeneralMessage request) {
String data = networkId.getId7() + memberOrClassId.getIdentifier()
+ request.getFullTableName() + request.getRecordId();
return hashWithPrefix(type, data);
}
/** SHA3-512 hashing with prefix from IdType */
private static String hashWithPrefix(IdType type, String data) {
byte[] sha = new SHA3.Digest512().digest(data.getBytes(StandardCharsets.UTF_8));
String suffix = HEX.formatHex(sha).substring(2); // keep 510 bits after 2-char prefix
return type.getHexPrefix() + suffix;
}
}
// ✅ Static reverse lookup table
private static final Map BY_PREFIX = new TreeMap<>();
static {
for (IdType type : IdType.values()) {
BY_PREFIX.put(type.getPrefixByte(), type);
}
}
public static IdType fromPrefixByte(byte prefixByte) {
return BY_PREFIX.get(prefixByte);
}
}