refactor: move brother_node development artifact to dev/test-nodes subdirectory

Development Artifact Cleanup:
 BROTHER_NODE REORGANIZATION: Moved development test node to appropriate location
- dev/test-nodes/brother_node/: Moved from root directory for better organization
- Contains development configuration, test logs, and test chain data
- No impact on production systems - purely development/testing artifact

 DEVELOPMENT ARTIFACTS IDENTIFIED:
- Chain ID: aitbc-brother-chain (test/development chain)
- Ports: 8010 (P2P) and 8011 (RPC) - different from production
- Environment: .env file with test configuration
- Logs: rpc.log and node.log from development testing session (March 15, 2026)

 ROOT DIRECTORY CLEANUP: Removed development clutter from production directory
- brother_node/ moved to dev/test-nodes/brother_node/
- Root directory now contains only production-ready components
- Development artifacts properly organized in dev/ subdirectory

DIRECTORY STRUCTURE IMPROVEMENT:
📁 dev/test-nodes/: Development and testing node configurations
🏗️ Root Directory: Clean production structure with only essential components
🧪 Development Isolation: Test environments separated from production

BENEFITS:
 Clean Production Directory: No development artifacts in root
 Better Organization: Development nodes grouped in dev/ subdirectory
 Clear Separation: Production vs development environments clearly distinguished
 Maintainability: Easier to identify and manage development components

RESULT: Successfully moved brother_node development artifact to dev/test-nodes/ subdirectory, cleaning up the root directory while preserving development testing environment for future use.
This commit is contained in:
2026-03-30 17:09:06 +02:00
parent bf730dcb4a
commit 816e258d4c
11734 changed files with 2001707 additions and 0 deletions

View File

@@ -0,0 +1,222 @@
import type { ZodTypeDef, ZodType } from "zod";
import { z } from "zod";
/**
* We use `unknown` here to avoid a circular dependency between the Hardhat and
* the Zod utils packages.
*/
export type HardhatUserConfigToValidate = unknown;
/**
* For the same reason, we duplicate the type here.
*/
export interface HardhatUserConfigValidationError {
path: Array<string | number>;
message: string;
}
/**
* A Zod untagged union type that returns a custom error message if the value
* is missing or invalid.
*
* WARNING: In most cases you should use {@link conditionalUnionType} instead.
*
* This union type is valid for simple cases, where the union is made of
* primitive or simple types.
*
* If you have a type that's complex, like an object or array, you must use
* {@link conditionalUnionType}.
*/
// TODO: improve the return type of this function to be more specific
export const unionType = (
types: Parameters<typeof z.union>[0],
errorMessage: string,
) =>
// NOTE: The reason we use `z.any().superRefine` instead of `z.union` is that
// we found a bug with the `z.union` method that causes it to return a
// "deeper" validation error, when we expected the `errorMessage`.
// See: https://github.com/colinhacks/zod/issues/2940#issuecomment-2380836931
z.any().superRefine((val, ctx) => {
if (types.some((t) => t.safeParse(val).success)) {
return;
}
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: errorMessage,
});
});
/**
* A Zod union type that allows you to provide hints to Zod about which of the
* type variant it should use.
*
* It receives an array of tuples, where each tuple contains a predicate
* function and a ZodType. The predicate function takes the data to be parsed
* and returns a boolean. If the predicate function returns true, the ZodType
* is used to parse the data.
*
* If none of the predicates returns true, an error is added to the context
* with the noMatchMessage message.
*
* For example, you can use this to conditionally validate a union type based
* on the values `typeof` and its fields:
*
* @example
* ```ts
* const fooType = conditionalUnionType(
* [
* [(data) => typeof data === "string", z.string()],
* [(data) => Array.isArray(data), z.array(z.string()).nonempty()],
* [(data) => isObject(data), z.object({foo: z.string().optional()})]
* ],
* "Expected a string, an array of strings, or an object with an optional 'foo' property",
* );
* ```
*
* @param cases An array of tuples of a predicate function and a ZodType.
* @param noMatchMessage THe error message to return if none of the predicates
* returns true.
* @returns The conditional union ZodType.
*/
// TODO: improve the return type of this function to be more specific
export const conditionalUnionType = (
cases: Array<[predicate: (data: unknown) => boolean, zodType: ZodType<any>]>,
noMatchMessage: string,
) =>
z.any().superRefine((data, ctx) => {
const matchingCase = cases.find(([predicate]) => predicate(data));
if (matchingCase === undefined) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: noMatchMessage,
});
return;
}
const zodeType = matchingCase[1];
const parsedData = zodeType.safeParse(data);
if (parsedData.error !== undefined) {
for (const issue of parsedData.error.issues) {
ctx.addIssue(issue);
}
}
});
/**
* Creates a Zod type to validate that a field of an object doesn't exist.
*
* This is useful when you have a {@link conditionalUnionType} that represents
* a union of object types with incompatible fields between each other.
*
* @example
* ```ts
* const typeWithFoo = z.object({
* foo: z.string(),
* bar: unexpecteddFieldType("This field is incompatible with `foo`"),
* });
*
* const typeWithBar = z.object({
* bar: z.string(),
* foo: unexpecteddFieldType("This field is incompatible with `bar`"),
* });
*
* const union = conditionalUnionType(
* [
* [(data) => isObject(data) && "foo" in data, typeWithFoo],
* [(data) => isObject(data) && "bar" in data, typeWithBar],
* ],
* "Expected an object with either a `foo` or a `bar` field",
* );
* ```
*
* @param errorMessage The error message to display if the field is present.
* @returns A Zod type that validates that a field of an object doesn't exist.
*/
export const incompatibleFieldType = (errorMessage = "Unexpectedd field") =>
z
.never({
errorMap: () => ({
message: errorMessage,
}),
})
.optional();
/**
* A Zod type to validate Hardhat's ConfigurationVariable objects.
*/
export const configurationVariableSchema = z.object({
_type: z.literal("ConfigurationVariable"),
name: z.string(),
});
/**
* A Zod type to validate Hardhat's ResolvedConfigurationVariable objects.
*/
export const resolvedConfigurationVariableSchema = z.object({
_type: z.literal("ResolvedConfigurationVariable"),
get: z.function(),
getUrl: z.function(),
getBigInt: z.function(),
getHexString: z.function(),
});
/**
* A Zod type to validate Hardhat's SensitiveString values.
*/
export const sensitiveStringSchema: z.ZodType<
string | z.infer<typeof configurationVariableSchema>
> = unionType(
[z.string(), configurationVariableSchema],
"Expected a string or a Configuration Variable",
);
/**
* A Zod type to validate Hardhat's SensitiveString values that expect a URL.
*
* TODO: The custom error message in the unionType function doesn't work
* correctly when using string().url() for validation, see:
* https://github.com/colinhacks/zod/issues/2940
* As a workaround, we provide the error message directly in the url() call.
* We should remove this when the issue is fixed.
*/
export const sensitiveUrlSchema: z.ZodType<
string | z.infer<typeof configurationVariableSchema>
> = unionType(
[
z.string().url("Expected a URL or a Configuration Variable"),
configurationVariableSchema,
],
"Expected a URL or a Configuration Variable",
);
/**
* A function to validate the user's configuration object against a Zod type.
*
* Note: The zod type MUST represent the HardhatUserConfig type, or a subset of
* it. You shouldn't use this function to validate their fields individually.
* The reason for this is that the paths of the validation errors must start
* from the root of the config object, so that they are correctly reported to
* the user.
*/
export function validateUserConfigZodType<
Output,
Def extends ZodTypeDef = ZodTypeDef,
Input = Output,
>(
hardhatUserConfig: HardhatUserConfigToValidate,
configType: ZodType<Output, Def, Input>,
): HardhatUserConfigValidationError[] {
const result = configType.safeParse(hardhatUserConfig);
if (result.success) {
return [];
} else {
return result.error.errors.map((issue) => ({
path: issue.path,
message: issue.message,
}));
}
}

View File

@@ -0,0 +1,11 @@
export * from "./utils.js";
export * from "./validate-params.js";
export * from "./types/access-list.js";
export * from "./types/address.js";
export * from "./types/any.js";
export * from "./types/authorization-list.js";
export * from "./types/data.js";
export * from "./types/hash.js";
export * from "./types/quantity.js";
export * from "./types/rpc-parity.js";
export * from "./types/tx-request.js";

View File

@@ -0,0 +1,20 @@
import type { ZodType } from "zod";
import { z } from "zod";
import { rpcData } from "./data.js";
const nullable = <T extends ZodType<any>>(schema: T) => schema.nullable();
const rpcAccessListTuple: ZodType<{
address: Uint8Array;
storageKeys: Uint8Array[] | null;
}> = z.object({
address: rpcData,
storageKeys: nullable(z.array(rpcData)),
});
export type RpcAccessListTuple = z.infer<typeof rpcAccessListTuple>;
export const rpcAccessList: ZodType<RpcAccessListTuple[]> =
z.array(rpcAccessListTuple);

View File

@@ -0,0 +1,26 @@
import type { ZodType } from "zod";
import { isAddress } from "@nomicfoundation/hardhat-utils/eth";
import { hexStringToBytes } from "@nomicfoundation/hardhat-utils/hex";
import { z } from "zod";
import { conditionalUnionType } from "@nomicfoundation/hardhat-zod-utils";
const ADDRESS_LENGTH_BYTES = 20;
export const rpcAddress: ZodType<Uint8Array> = conditionalUnionType(
[
[
(data) => Buffer.isBuffer(data) && data.length === ADDRESS_LENGTH_BYTES,
z.instanceof(Uint8Array),
],
[isAddress, z.string()],
],
"Expected a Buffer with correct length or a valid RPC address string",
).transform((v) => (typeof v === "string" ? hexStringToBytes(v) : v));
export const nullableRpcAddress: ZodType<Uint8Array | null> = rpcAddress
.or(z.null())
.describe(
"Expected a Buffer with correct length, a valid RPC address string, or the null value",
);

View File

@@ -0,0 +1,5 @@
import type { ZodType } from "zod";
import { z } from "zod";
export const rpcAny: ZodType<any> = z.any();

View File

@@ -0,0 +1,31 @@
import type { ZodType } from "zod";
import { z } from "zod";
import { rpcAddress } from "./address.js";
import { rpcHash } from "./hash.js";
import { rpcQuantity } from "./quantity.js";
import { rpcParity } from "./rpc-parity.js";
const rpcAuthorizationListTuple: ZodType<{
chainId: bigint;
address: Uint8Array;
nonce: bigint;
yParity: Uint8Array;
r: Uint8Array;
s: Uint8Array;
}> = z.object({
chainId: rpcQuantity,
address: rpcAddress,
nonce: rpcQuantity,
yParity: rpcParity,
r: rpcHash,
s: rpcHash,
});
export type RpcAuthorizationListTuple = z.infer<
typeof rpcAuthorizationListTuple
>;
export const rpcAuthorizationList: ZodType<RpcAuthorizationListTuple[]> =
z.array(rpcAuthorizationListTuple);

View File

@@ -0,0 +1,16 @@
import type { ZodType } from "zod";
import { hexStringToBytes } from "@nomicfoundation/hardhat-utils/hex";
import { z } from "zod";
import { conditionalUnionType } from "@nomicfoundation/hardhat-zod-utils";
import { isRpcDataString } from "../utils.js";
export const rpcData: ZodType<Uint8Array> = conditionalUnionType(
[
[Buffer.isBuffer, z.instanceof(Uint8Array)],
[isRpcDataString, z.string()],
],
"Expected a Buffer or a valid RPC data string",
).transform((v) => (typeof v === "string" ? hexStringToBytes(v) : v));

View File

@@ -0,0 +1,22 @@
import type { ZodType } from "zod";
import { isHash } from "@nomicfoundation/hardhat-utils/eth";
import { hexStringToBytes } from "@nomicfoundation/hardhat-utils/hex";
import { z } from "zod";
import { conditionalUnionType } from "@nomicfoundation/hardhat-zod-utils";
const HASH_LENGTH_BYTES = 32;
export const rpcHash: ZodType<Uint8Array> = conditionalUnionType(
[
[
(data) => Buffer.isBuffer(data) && data.length === HASH_LENGTH_BYTES,
z.instanceof(Uint8Array),
],
[isHash, z.string()],
],
"Expected a Buffer with the correct length or a valid RPC hash string",
).transform((v) =>
typeof v === "string" ? Buffer.from(hexStringToBytes(v)) : v,
);

View File

@@ -0,0 +1,15 @@
import type { ZodType } from "zod";
import { z } from "zod";
import { conditionalUnionType } from "@nomicfoundation/hardhat-zod-utils";
import { isRpcQuantityString } from "../utils.js";
export const rpcQuantity: ZodType<bigint> = conditionalUnionType(
[
[(data) => typeof data === "bigint", z.bigint()],
[isRpcQuantityString, z.string()],
],
"Expected a bigint or a valid RPC quantity string",
).transform((v) => (typeof v === "string" ? BigInt(v) : v));

View File

@@ -0,0 +1,25 @@
import type { ZodType } from "zod";
import { hexStringToBytes } from "@nomicfoundation/hardhat-utils/hex";
import { z } from "zod";
import { conditionalUnionType } from "@nomicfoundation/hardhat-zod-utils";
const PARITY_LENGTH_BYTES = 1;
export const rpcParity: ZodType<Buffer> = conditionalUnionType(
[
[
(data) => Buffer.isBuffer(data) && data.length === PARITY_LENGTH_BYTES,
z.instanceof(Uint8Array),
],
[isRpcParityString, z.string()],
],
"Expected a Buffer or valid parity string",
).transform((v) =>
typeof v === "string" ? Buffer.from(hexStringToBytes(v)) : v,
);
function isRpcParityString(u: unknown): u is string {
return typeof u === "string" && u.match(/^0x[0-9a-fA-F]{1,2}$/) !== null;
}

View File

@@ -0,0 +1,53 @@
import type { ZodType } from "zod";
import { z } from "zod";
import { rpcAccessList } from "./access-list.js";
import { nullableRpcAddress, rpcAddress } from "./address.js";
import { rpcAuthorizationList } from "./authorization-list.js";
import { rpcData } from "./data.js";
import { rpcHash } from "./hash.js";
import { rpcQuantity } from "./quantity.js";
const optional = <T extends ZodType<any>>(schema: T) => schema.optional();
export interface RpcTransactionRequest {
from: Uint8Array;
to?: Uint8Array | null;
gas?: bigint;
gasPrice?: bigint;
value?: bigint;
nonce?: bigint;
data?: Uint8Array;
accessList?: Array<{ address: Uint8Array; storageKeys: Uint8Array[] | null }>;
chainId?: bigint;
maxFeePerGas?: bigint;
maxPriorityFeePerGas?: bigint;
blobs?: Uint8Array[];
blobVersionedHashes?: Uint8Array[];
authorizationList?: Array<{
chainId: bigint;
address: Uint8Array;
nonce: bigint;
yParity: Uint8Array;
r: Uint8Array;
s: Uint8Array;
}>;
}
export const rpcTransactionRequest: ZodType<RpcTransactionRequest> = z.object({
from: rpcAddress,
to: optional(nullableRpcAddress),
gas: optional(rpcQuantity),
gasPrice: optional(rpcQuantity),
value: optional(rpcQuantity),
nonce: optional(rpcQuantity),
data: optional(rpcData),
accessList: optional(rpcAccessList),
chainId: optional(rpcQuantity),
maxFeePerGas: optional(rpcQuantity),
maxPriorityFeePerGas: optional(rpcQuantity),
blobs: optional(z.array(rpcData)),
blobVersionedHashes: optional(z.array(rpcHash)),
authorizationList: optional(rpcAuthorizationList),
});

View File

@@ -0,0 +1,9 @@
export function isRpcQuantityString(u: unknown): u is string {
return (
typeof u === "string" && /^0x(?:0|(?:[1-9a-fA-F][0-9a-fA-F]*))$/.test(u)
);
}
export function isRpcDataString(u: unknown): u is string {
return typeof u === "string" && /^0x(?:[0-9a-fA-F]{2})*$/.test(u);
}

View File

@@ -0,0 +1,62 @@
import type { ZodType } from "zod";
import { HardhatError } from "@nomicfoundation/hardhat-errors";
export function validateParams<TypesT extends ReadonlyArray<ZodType<any>>>(
params: any[],
...types: TypesT
): {
[i in keyof TypesT]: TypesT[i] extends ZodType<infer TypeT> ? TypeT : never;
} {
if (types === undefined && params.length > 0) {
throw new HardhatError(
HardhatError.ERRORS.CORE.NETWORK.WRONG_VALIDATION_PARAMS,
{
reason: `No argument was expected and got ${params.length}`,
},
);
}
let optionalParams = 0;
for (let i = types.length - 1; i >= 0; i--) {
if (types[i].isOptional() || types[i].isNullable()) {
optionalParams += 1;
} else {
break;
}
}
if (optionalParams === 0) {
if (params.length !== types.length) {
throw new HardhatError(
HardhatError.ERRORS.CORE.NETWORK.WRONG_VALIDATION_PARAMS,
{
reason: `Expected exactly ${types.length} arguments and got ${params.length}`,
},
);
}
} else {
if (
params.length > types.length ||
params.length < types.length - optionalParams
) {
throw new HardhatError(
HardhatError.ERRORS.CORE.NETWORK.WRONG_VALIDATION_PARAMS,
{
reason: `Expected between ${types.length - optionalParams} and ${
types.length
} arguments and got ${params.length}`,
},
);
}
}
const decoded: any[] = [];
for (let i = 0; i < types.length; i++) {
const res = types[i].parse(params[i]);
decoded.push(res);
}
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TypeScript can't infer the complex return type
return decoded as any;
}