0.1.0
This commit is contained in:
116
migration/khadhroony-bot2-reference/scripts/audit_khadhroony_workspace_rules.py
Executable file
116
migration/khadhroony-bot2-reference/scripts/audit_khadhroony_workspace_rules.py
Executable file
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python3
|
||||
# file: scripts/audit_khadhroony_workspace_rules.py
|
||||
# version: 1
|
||||
|
||||
"""Audit mechanically verifiable rules specific to khadhroony-bot2."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import dataclasses
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
import tomllib
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class Violation:
|
||||
"""One project-specific workspace rule violation."""
|
||||
|
||||
code: str
|
||||
path: str
|
||||
line: int
|
||||
message: str
|
||||
|
||||
|
||||
def package_name(crate_dir: pathlib.Path) -> str | None:
|
||||
"""Return a crate package name."""
|
||||
|
||||
cargo = crate_dir / "Cargo.toml"
|
||||
if not cargo.exists():
|
||||
return None
|
||||
value = tomllib.loads(cargo.read_text(encoding="utf-8")).get("package", {}).get("name")
|
||||
return value if isinstance(value, str) else None
|
||||
|
||||
|
||||
def relevant_tracing_crate(crate_dir: pathlib.Path) -> bool:
|
||||
"""Return whether the crate owns a tracing target contract."""
|
||||
|
||||
cargo = crate_dir / "Cargo.toml"
|
||||
data = tomllib.loads(cargo.read_text(encoding="utf-8"))
|
||||
if "tracing" in data.get("dependencies", {}):
|
||||
return True
|
||||
return any("TRACING_TARGET" in path.read_text(encoding="utf-8") for path in crate_dir.glob("src/**/*.rs"))
|
||||
|
||||
|
||||
def audit_tracing(root: pathlib.Path) -> list[Violation]:
|
||||
"""Audit the project tracing target contract."""
|
||||
|
||||
violations: list[Violation] = []
|
||||
for cargo in sorted(root.glob("*/Cargo.toml")):
|
||||
crate = cargo.parent
|
||||
if not relevant_tracing_crate(crate):
|
||||
continue
|
||||
name = package_name(crate)
|
||||
if name is None:
|
||||
continue
|
||||
constants = crate / "src/constants.rs"
|
||||
expected = f'pub(crate) const TRACING_TARGET: &str = "{name}";'
|
||||
relative_constants = constants.relative_to(root).as_posix()
|
||||
if not constants.exists() or expected not in constants.read_text(encoding="utf-8"):
|
||||
violations.append(Violation("KH_TRACE001", relative_constants, 1, f"expected `{expected}`"))
|
||||
crate_root = crate / "src/lib.rs"
|
||||
if not crate_root.exists():
|
||||
crate_root = crate / "src/main.rs"
|
||||
relative_root = crate_root.relative_to(root).as_posix()
|
||||
if not crate_root.exists() or "pub(crate) use crate::constants::TRACING_TARGET;" not in crate_root.read_text(encoding="utf-8"):
|
||||
violations.append(Violation("KH_TRACE002", relative_root, 1, "missing crate-root TRACING_TARGET re-export"))
|
||||
for path in crate.glob("src/**/*.rs"):
|
||||
relative = path.relative_to(root).as_posix()
|
||||
for index, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
|
||||
if "khbot." in line:
|
||||
violations.append(Violation("KH_TRACE003", relative, index, "legacy khbot tracing target is forbidden"))
|
||||
if "crate::constants::TRACING_TARGET" in line and path.name not in {"lib.rs", "main.rs"}:
|
||||
violations.append(Violation("KH_TRACE004", relative, index, "use crate::TRACING_TARGET outside crate root"))
|
||||
if re.search(r"(?:crate::[A-Za-z0-9_]+::|(?<!crate::))TRACING_TARGET", line) and "const TRACING_TARGET" not in line and "use crate::constants::TRACING_TARGET" not in line:
|
||||
if "crate::TRACING_TARGET" not in line:
|
||||
violations.append(Violation("KH_TRACE005", relative, index, "non-canonical TRACING_TARGET path"))
|
||||
return violations
|
||||
|
||||
|
||||
def audit_solana_types(root: pathlib.Path) -> list[Violation]:
|
||||
"""Reject non-canonical Solana address types in workspace Rust code."""
|
||||
|
||||
violations: list[Violation] = []
|
||||
for path in sorted(root.rglob("*.rs")):
|
||||
if "target" in path.parts or ".git" in path.parts:
|
||||
continue
|
||||
relative = path.relative_to(root).as_posix()
|
||||
for index, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
|
||||
if "solana_address::Address" in line:
|
||||
violations.append(Violation("KH_SOL001", relative, index, "use solana_pubkey::Pubkey instead of solana_address::Address"))
|
||||
return violations
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Run the khadhroony-specific audit."""
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--root", default=".")
|
||||
parser.add_argument("--report-only", action="store_true")
|
||||
arguments = parser.parse_args()
|
||||
root = pathlib.Path(arguments.root).resolve()
|
||||
violations = audit_tracing(root) + audit_solana_types(root)
|
||||
violations.sort(key=lambda item: (item.code, item.path, item.line, item.message))
|
||||
if not violations:
|
||||
sys.stdout.write("Khadhroony workspace rule audit: clean\n")
|
||||
return 0
|
||||
sys.stdout.write(f"Khadhroony workspace rule audit: {len(violations)} violation(s)\n")
|
||||
for item in violations:
|
||||
sys.stdout.write(f"{item.code} {item.path}:{item.line}: {item.message}\n")
|
||||
return 0 if arguments.report_only else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
174
migration/khadhroony-bot2-reference/scripts/audit_rust_export_completeness.py
Executable file
174
migration/khadhroony-bot2-reference/scripts/audit_rust_export_completeness.py
Executable file
@@ -0,0 +1,174 @@
|
||||
#!/usr/bin/env python3
|
||||
# file: scripts/audit_rust_export_completeness.py
|
||||
# version: 2
|
||||
|
||||
"""Report missing crate-root exports and replaceable long internal paths.
|
||||
|
||||
The audit distinguishes declarations from their crate-root aliases. Crate-root
|
||||
files are excluded from long-path findings because re-export declarations must
|
||||
name their source module. Findings remain advisory until the affected crate is
|
||||
compiled.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import dataclasses
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class Candidate:
|
||||
"""One export-completeness candidate."""
|
||||
|
||||
code: str
|
||||
path: str
|
||||
line: int
|
||||
message: str
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class Declaration:
|
||||
"""One module-level public or crate-public declaration."""
|
||||
|
||||
module: str
|
||||
name: str
|
||||
visibility: str
|
||||
path: pathlib.Path
|
||||
line: int
|
||||
|
||||
|
||||
def crate_roots(root: pathlib.Path) -> list[pathlib.Path]:
|
||||
"""Return workspace crate directories that contain a Rust crate root."""
|
||||
|
||||
crates: list[pathlib.Path] = []
|
||||
for manifest in root.glob("*/Cargo.toml"):
|
||||
crate = manifest.parent
|
||||
if (crate / "src/lib.rs").is_file() or (crate / "src/main.rs").is_file():
|
||||
crates.append(crate)
|
||||
return sorted(crates)
|
||||
|
||||
|
||||
def module_path(crate: pathlib.Path, path: pathlib.Path) -> str:
|
||||
"""Return the Rust module path represented by one source file."""
|
||||
|
||||
relative = path.relative_to(crate / "src").with_suffix("")
|
||||
return "::".join(relative.parts)
|
||||
|
||||
|
||||
def declaration_candidates(crate: pathlib.Path, path: pathlib.Path) -> list[Declaration]:
|
||||
"""Return module-level public declarations from one Rust module."""
|
||||
|
||||
text = path.read_text(encoding="utf-8")
|
||||
depth = 0
|
||||
found: list[Declaration] = []
|
||||
pattern = re.compile(
|
||||
r"^\s*(pub(?:\(crate\))?)\s+(?:(?:async|unsafe|const)\s+)*(?:const|static|type|struct|enum|trait|union|fn)\s+([A-Za-z_][A-Za-z0-9_]*)"
|
||||
)
|
||||
for number, line in enumerate(text.splitlines(), 1):
|
||||
if depth == 0:
|
||||
match = pattern.match(line)
|
||||
if match is not None:
|
||||
found.append(
|
||||
Declaration(
|
||||
module_path(crate, path),
|
||||
match.group(2),
|
||||
match.group(1),
|
||||
path,
|
||||
number,
|
||||
)
|
||||
)
|
||||
depth += line.count("{") - line.count("}")
|
||||
if depth < 0:
|
||||
depth = 0
|
||||
return found
|
||||
|
||||
|
||||
def root_exports(crate_root: pathlib.Path) -> dict[tuple[str, str], str]:
|
||||
"""Return source module/name pairs and their crate-root aliases."""
|
||||
|
||||
exports: dict[tuple[str, str], str] = {}
|
||||
pattern = re.compile(
|
||||
r"^\s*pub(?:\(crate\))?\s+use\s+crate::([A-Za-z_][A-Za-z0-9_:]*)::([A-Za-z_][A-Za-z0-9_]*)(?:\s+as\s+([A-Za-z_][A-Za-z0-9_]*))?\s*;",
|
||||
re.MULTILINE,
|
||||
)
|
||||
for match in pattern.finditer(crate_root.read_text(encoding="utf-8")):
|
||||
exports[(match.group(1), match.group(2))] = match.group(3) or match.group(2)
|
||||
return exports
|
||||
|
||||
|
||||
def audit_crate(workspace: pathlib.Path, crate: pathlib.Path) -> list[Candidate]:
|
||||
"""Audit one crate and return advisory candidates."""
|
||||
|
||||
crate_root = crate / "src/lib.rs"
|
||||
if not crate_root.is_file():
|
||||
crate_root = crate / "src/main.rs"
|
||||
exports = root_exports(crate_root)
|
||||
candidates: list[Candidate] = []
|
||||
declarations: dict[tuple[str, str], Declaration] = {}
|
||||
for path in sorted((crate / "src").rglob("*.rs")):
|
||||
if path == crate_root:
|
||||
continue
|
||||
for declaration in declaration_candidates(crate, path):
|
||||
key = (declaration.module, declaration.name)
|
||||
declarations[key] = declaration
|
||||
if key not in exports:
|
||||
relative = path.relative_to(workspace).as_posix()
|
||||
candidates.append(
|
||||
Candidate(
|
||||
"EXPORT001",
|
||||
relative,
|
||||
declaration.line,
|
||||
f"{declaration.visibility} `{declaration.name}` has no crate-root re-export",
|
||||
)
|
||||
)
|
||||
long_path = re.compile(
|
||||
r"\bcrate::([A-Za-z_][A-Za-z0-9_:]*)::([A-Za-z_][A-Za-z0-9_]*)"
|
||||
)
|
||||
for path in sorted((crate / "src").rglob("*.rs")):
|
||||
if path == crate_root:
|
||||
continue
|
||||
for number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
|
||||
for match in long_path.finditer(line):
|
||||
key = (match.group(1), match.group(2))
|
||||
alias = exports.get(key)
|
||||
if alias is None:
|
||||
continue
|
||||
relative = path.relative_to(workspace).as_posix()
|
||||
candidates.append(
|
||||
Candidate(
|
||||
"EXPORT002",
|
||||
relative,
|
||||
number,
|
||||
f"long internal path `crate::{match.group(1)}::{match.group(2)}` must use `crate::{alias}`",
|
||||
)
|
||||
)
|
||||
return candidates
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Run the advisory export-completeness audit."""
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--root", default=".")
|
||||
parser.add_argument("--summary-only", action="store_true")
|
||||
arguments = parser.parse_args()
|
||||
workspace = pathlib.Path(arguments.root).resolve()
|
||||
candidates = [item for crate in crate_roots(workspace) for item in audit_crate(workspace, crate)]
|
||||
candidates.sort(key=lambda item: (item.code, item.path, item.line, item.message))
|
||||
counts: dict[str, int] = {}
|
||||
for item in candidates:
|
||||
counts[item.code] = counts.get(item.code, 0) + 1
|
||||
sys.stdout.write(f"Rust export completeness audit: {len(candidates)} candidate(s)\n")
|
||||
for code in sorted(counts):
|
||||
sys.stdout.write(f"{code}: {counts[code]}\n")
|
||||
if not arguments.summary_only:
|
||||
for item in candidates:
|
||||
sys.stdout.write(f"{item.code} {item.path}:{item.line}: {item.message}\n")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
100
migration/khadhroony-bot2-reference/scripts/audit_rust_general_rules.py
Executable file
100
migration/khadhroony-bot2-reference/scripts/audit_rust_general_rules.py
Executable file
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
# file: scripts/audit_rust_general_rules.py
|
||||
# version: 2
|
||||
|
||||
"""Audit mechanically verifiable Rust rules shared by all Rust projects."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import dataclasses
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class Violation:
|
||||
"""One mechanically detected general Rust rule violation."""
|
||||
|
||||
code: str
|
||||
path: str
|
||||
line: int
|
||||
message: str
|
||||
|
||||
|
||||
def rust_files(root: pathlib.Path) -> list[pathlib.Path]:
|
||||
"""Return Rust sources outside generated build directories."""
|
||||
|
||||
return sorted(
|
||||
path
|
||||
for path in root.rglob("*.rs")
|
||||
if "target" not in path.parts
|
||||
and ".git" not in path.parts
|
||||
and "mnt" not in path.relative_to(root).parts
|
||||
and not any(part.startswith("pre035_") for part in path.relative_to(root).parts)
|
||||
)
|
||||
|
||||
|
||||
def audit_file(root: pathlib.Path, path: pathlib.Path) -> list[Violation]:
|
||||
"""Audit one Rust source against general rules."""
|
||||
|
||||
relative = path.relative_to(root).as_posix()
|
||||
text = path.read_text(encoding="utf-8")
|
||||
lines = text.splitlines()
|
||||
violations: list[Violation] = []
|
||||
expected_header = f"// file: {relative}"
|
||||
if not lines or lines[0] != expected_header:
|
||||
violations.append(Violation("RUST001", relative, 1, f"expected `{expected_header}`"))
|
||||
if len(lines) < 2 or re.fullmatch(r"// version: [1-9][0-9]*", lines[1]) is None:
|
||||
violations.append(Violation("RUST002", relative, 2, "missing positive file version"))
|
||||
if not text.endswith("\n") or text.endswith("\n\n"):
|
||||
violations.append(Violation("RUST003", relative, max(len(lines), 1), "file must end with exactly one newline"))
|
||||
for index, line in enumerate(lines, 1):
|
||||
stripped = line.strip()
|
||||
if re.match(r"^(?:pub(?:\(crate\))?\s+)?use\s+", stripped) is None:
|
||||
continue
|
||||
is_export = stripped.startswith("pub use ") or stripped.startswith("pub(crate) use ")
|
||||
justified_trait = "rust-rules: trait-import" in stripped
|
||||
justified_derive = "rust-rules: derive-import" in stripped
|
||||
if not is_export and not justified_trait and not justified_derive:
|
||||
violations.append(Violation("RUST010", relative, index, "ordinary use requires a trait or derive import justification"))
|
||||
if "::*" in stripped:
|
||||
violations.append(Violation("RUST012", relative, index, "glob imports are forbidden"))
|
||||
if "{" in stripped or "}" in stripped:
|
||||
code = "RUST011" if is_export else "RUST013"
|
||||
violations.append(Violation(code, relative, index, "grouped use declarations are forbidden"))
|
||||
if path.name in {"lib.rs", "main.rs"}:
|
||||
for attribute in ("#![warn(missing_docs)]", "#![deny(unreachable_pub)]", "#![forbid(unsafe_code)]"):
|
||||
if attribute not in lines[:20]:
|
||||
violations.append(Violation("RUST020", relative, 1, f"missing `{attribute}`"))
|
||||
for index, line in enumerate(lines, 1):
|
||||
if not (line.startswith("pub use ") or line.startswith("pub(crate) use ")):
|
||||
continue
|
||||
previous = lines[index - 2].strip() if index >= 2 else ""
|
||||
if not previous.startswith("///"):
|
||||
violations.append(Violation("RUST021", relative, index, "crate-root re-export requires adjacent rustdoc"))
|
||||
return violations
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Run the general Rust audit."""
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--root", default=".")
|
||||
parser.add_argument("--report-only", action="store_true")
|
||||
arguments = parser.parse_args()
|
||||
root = pathlib.Path(arguments.root).resolve()
|
||||
violations = [item for path in rust_files(root) for item in audit_file(root, path)]
|
||||
violations.sort(key=lambda item: (item.code, item.path, item.line, item.message))
|
||||
if not violations:
|
||||
sys.stdout.write("General Rust rule audit: clean\n")
|
||||
return 0
|
||||
sys.stdout.write(f"General Rust rule audit: {len(violations)} violation(s)\n")
|
||||
for item in violations:
|
||||
sys.stdout.write(f"{item.code} {item.path}:{item.line}: {item.message}\n")
|
||||
return 0 if arguments.report_only else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
33
migration/khadhroony-bot2-reference/scripts/audit_rust_workspace_rules.py
Executable file
33
migration/khadhroony-bot2-reference/scripts/audit_rust_workspace_rules.py
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python3
|
||||
# file: scripts/audit_rust_workspace_rules.py
|
||||
# version: 2
|
||||
|
||||
"""Run both the general Rust and khadhroony-specific workspace audits."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import pathlib
|
||||
import subprocess
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Run both independent audit scripts."""
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--root", default=".")
|
||||
parser.add_argument("--report-only", action="store_true")
|
||||
arguments = parser.parse_args()
|
||||
script_dir = pathlib.Path(__file__).resolve().parent
|
||||
commands = [
|
||||
["python3", str(script_dir / "audit_rust_general_rules.py"), "--root", arguments.root],
|
||||
["python3", str(script_dir / "audit_khadhroony_workspace_rules.py"), "--root", arguments.root],
|
||||
]
|
||||
if arguments.report_only:
|
||||
for command in commands:
|
||||
command.append("--report-only")
|
||||
return max(subprocess.run(command, check=False).returncode for command in commands)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
# file: scripts/devnet/load_elgamal_registry_fixture.sh
|
||||
# version: 1
|
||||
set -euo pipefail
|
||||
|
||||
PROFILE="${KB_ELGAMAL_PROFILE:-local_devnet}"
|
||||
FIXTURE_DIR="${KB_ELGAMAL_FIXTURE_DIR:-./wallets/temporary/${PROFILE}/elgamal_registry_validation}"
|
||||
FIXTURE_FILE="${FIXTURE_DIR}/fixture.env"
|
||||
|
||||
if [[ ! -r "$FIXTURE_FILE" ]]; then
|
||||
printf 'Fixture ElGamal Registry absente: %s\n' "$FIXTURE_FILE" >&2
|
||||
return 1 2>/dev/null || exit 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
source "$FIXTURE_FILE"
|
||||
required_variables=(
|
||||
KB_ELGAMAL_CLUSTER_URL
|
||||
TOKEN_2022_PROGRAM
|
||||
ELGAMAL_REGISTRY_PROGRAM
|
||||
ZK_ELGAMAL_PROOF_PROGRAM
|
||||
ELGAMAL_REGISTRY_ADDRESS
|
||||
ELGAMAL_PUBKEY_BASE64
|
||||
PUBKEY_VALIDITY_PROOF_CONTEXT
|
||||
TOKEN_2022_CONFIDENTIAL_ACCOUNT
|
||||
)
|
||||
for variable in "${required_variables[@]}"; do
|
||||
if [[ -z "${!variable:-}" ]]; then
|
||||
printf 'Fixture incomplète, variable absente: %s\n' "$variable" >&2
|
||||
return 1 2>/dev/null || exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
printf 'Fixture: %s\n' "$FIXTURE_FILE"
|
||||
printf 'Cluster: %s\n' "$KB_ELGAMAL_CLUSTER_URL"
|
||||
printf 'Registry program: %s\n' "$ELGAMAL_REGISTRY_PROGRAM"
|
||||
printf 'Registry PDA: %s\n' "$ELGAMAL_REGISTRY_ADDRESS"
|
||||
printf 'Proof context: %s\n' "$PUBKEY_VALIDITY_PROOF_CONTEXT"
|
||||
printf 'Token-2022 confidential account: %s\n' "$TOKEN_2022_CONFIDENTIAL_ACCOUNT"
|
||||
24
migration/khadhroony-bot2-reference/scripts/devnet/load_spl_fixture.sh
Executable file
24
migration/khadhroony-bot2-reference/scripts/devnet/load_spl_fixture.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
# file: scripts/devnet/load_spl_fixture.sh
|
||||
# version: 2
|
||||
family="${1:-token-2022}"
|
||||
workspace="${KB_WORKSPACE_ROOT:-$(pwd)}"
|
||||
wallet_dir="${KB_DEVNET_WALLET_DIR:-$workspace/wallets/temporary/local_devnet}"
|
||||
case "$family" in
|
||||
token-2022) fixture="$wallet_dir/spl_token_2022_validation/fixture.env"; prefix='TOKEN_2022' ;;
|
||||
classic) fixture="$wallet_dir/spl_token_classic_validation/fixture.env"; prefix='TOKEN_CLASSIC' ;;
|
||||
*) printf 'Usage: source %s token-2022|classic\n' "${BASH_SOURCE[0]}" >&2; return 64 2>/dev/null || exit 64 ;;
|
||||
esac
|
||||
test -f "$fixture"
|
||||
# shellcheck disable=SC1090
|
||||
source "$fixture"
|
||||
program_var="${prefix}_PROGRAM"; mint_var="${prefix}_MINT"; source_var="${prefix}_SOURCE"; destination_var="${prefix}_DESTINATION"; delegate_var="${prefix}_DELEGATE"; close_var="${prefix}_CLOSE_ACCOUNT"; ata_var="${prefix}_ATA"
|
||||
program="${!program_var}"; mint="${!mint_var}"; source_account="${!source_var}"; destination="${!destination_var}"; delegate="${!delegate_var}"; close_account="${!close_var}"; ata="${!ata_var}"
|
||||
for value in "$program" "$mint" "$source_account" "$destination" "$delegate" "$close_account" "$ata"; do
|
||||
[[ "$value" =~ ^[1-9A-HJ-NP-Za-km-z]{32,44}$ ]] || { printf 'Adresse Base58 invalide: %s\n' "$value" >&2; return 65 2>/dev/null || exit 65; }
|
||||
done
|
||||
printf 'Fixture: %s\nWallet: %s\nProgram: %s\nMint: %s\nSource: %s\nDestination: %s\nDelegate: %s\nClose account: %s\nATA: %s\n' "$fixture" "$KB_DEVNET_WALLET_ADDRESS" "$program" "$mint" "$source_account" "$destination" "$delegate" "$close_account" "$ata"
|
||||
spl-token --url devnet --program-id "$program" display "$mint" | sed -n '1,8p'
|
||||
spl-token --url devnet --program-id "$program" display "$source_account" | sed -n '1,12p'
|
||||
spl-token --url devnet --program-id "$program" display "$destination" | sed -n '1,12p'
|
||||
spl-token --url devnet --program-id "$program" display "$close_account" | sed -n '1,12p'
|
||||
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env bash
|
||||
# file: scripts/devnet/prepare_elgamal_registry_fixture.sh
|
||||
# version: 1
|
||||
set -euo pipefail
|
||||
|
||||
CLUSTER_URL="${KB_ELGAMAL_CLUSTER_URL:-https://api.devnet.solana.com}"
|
||||
PROFILE="${KB_ELGAMAL_PROFILE:-local_devnet}"
|
||||
FIXTURE_DIR="${KB_ELGAMAL_FIXTURE_DIR:-./wallets/temporary/${PROFILE}/elgamal_registry_validation}"
|
||||
FIXTURE_FILE="${FIXTURE_DIR}/fixture.env"
|
||||
TOKEN_2022_PROGRAM="${TOKEN_2022_PROGRAM:-TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb}"
|
||||
ELGAMAL_REGISTRY_PROGRAM="${ELGAMAL_REGISTRY_PROGRAM:-regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg}"
|
||||
ZK_ELGAMAL_PROOF_PROGRAM="${ZK_ELGAMAL_PROOF_PROGRAM:-ZkE1Gama1Proof11111111111111111111111111111}"
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage:
|
||||
ELGAMAL_REGISTRY_ADDRESS=<PDA> \\
|
||||
ELGAMAL_PUBKEY_BASE64=<base64-32-bytes> \\
|
||||
PUBKEY_VALIDITY_PROOF_CONTEXT=<pubkey> \\
|
||||
TOKEN_2022_CONFIDENTIAL_ACCOUNT=<pubkey> \\
|
||||
scripts/devnet/prepare_elgamal_registry_fixture.sh [--force]
|
||||
|
||||
The script validates and records an already generated cryptographic fixture.
|
||||
It never fabricates an ElGamal key or a zero-knowledge proof.
|
||||
USAGE
|
||||
}
|
||||
|
||||
FORCE=false
|
||||
if [[ "${1:-}" == "--force" ]]; then
|
||||
FORCE=true
|
||||
elif [[ $# -ne 0 ]]; then
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
|
||||
for command in solana base64; do
|
||||
if ! command -v "$command" >/dev/null 2>&1; then
|
||||
printf 'Commande requise absente: %s\n' "$command" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
required_variables=(
|
||||
ELGAMAL_REGISTRY_ADDRESS
|
||||
ELGAMAL_PUBKEY_BASE64
|
||||
PUBKEY_VALIDITY_PROOF_CONTEXT
|
||||
TOKEN_2022_CONFIDENTIAL_ACCOUNT
|
||||
)
|
||||
for variable in "${required_variables[@]}"; do
|
||||
if [[ -z "${!variable:-}" ]]; then
|
||||
printf 'Variable requise absente: %s\n' "$variable" >&2
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
validate_existing_account() {
|
||||
local label="$1"
|
||||
local value="$2"
|
||||
if ! solana account "$value" --url "$CLUSTER_URL" --output json-compact >/dev/null 2>&1; then
|
||||
printf '%s non lisible sur %s: %s\n' "$label" "$CLUSTER_URL" "$value" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
validate_registry_address() {
|
||||
local output
|
||||
if output="$(solana account "$ELGAMAL_REGISTRY_ADDRESS" --url "$CLUSTER_URL" --output json-compact 2>&1)"; then
|
||||
printf 'Registry déjà présent; la fixture conviendra à UpdateRegistry mais CreateRegistry doit utiliser un nouveau propriétaire/PDA.\n' >&2
|
||||
return 0
|
||||
fi
|
||||
if [[ "$output" == *"AccountNotFound"* ]]; then
|
||||
return 0
|
||||
fi
|
||||
printf 'Adresse Registry invalide ou RPC indisponible: %s\n%s\n' "$ELGAMAL_REGISTRY_ADDRESS" "$output" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ! decoded_length="$(printf '%s' "$ELGAMAL_PUBKEY_BASE64" | base64 --decode 2>/dev/null | wc -c)"; then
|
||||
printf 'ELGAMAL_PUBKEY_BASE64 n\x27est pas un Base64 valide.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$decoded_length" -ne 32 ]]; then
|
||||
printf 'ELGAMAL_PUBKEY_BASE64 doit décoder exactement 32 octets; obtenu: %s.\n' "$decoded_length" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
validate_registry_address
|
||||
validate_existing_account "Proof Context State PubkeyValidity" "$PUBKEY_VALIDITY_PROOF_CONTEXT"
|
||||
validate_existing_account "Token-2022 confidential account" "$TOKEN_2022_CONFIDENTIAL_ACCOUNT"
|
||||
|
||||
for program in "$TOKEN_2022_PROGRAM" "$ELGAMAL_REGISTRY_PROGRAM" "$ZK_ELGAMAL_PROOF_PROGRAM"; do
|
||||
if ! solana program show "$program" --url "$CLUSTER_URL" >/dev/null 2>&1; then
|
||||
printf 'Programme requis absent ou non exécutable sur %s: %s\n' "$CLUSTER_URL" "$program" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -e "$FIXTURE_FILE" && "$FORCE" != true ]]; then
|
||||
printf 'Fixture déjà présente: %s; utiliser --force pour la remplacer.\n' "$FIXTURE_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$FIXTURE_DIR"
|
||||
umask 077
|
||||
cat > "$FIXTURE_FILE" <<ENV
|
||||
export KB_ELGAMAL_CLUSTER_URL='$CLUSTER_URL'
|
||||
export TOKEN_2022_PROGRAM='$TOKEN_2022_PROGRAM'
|
||||
export ELGAMAL_REGISTRY_PROGRAM='$ELGAMAL_REGISTRY_PROGRAM'
|
||||
export ZK_ELGAMAL_PROOF_PROGRAM='$ZK_ELGAMAL_PROOF_PROGRAM'
|
||||
export ELGAMAL_REGISTRY_ADDRESS='$ELGAMAL_REGISTRY_ADDRESS'
|
||||
export ELGAMAL_PUBKEY_BASE64='$ELGAMAL_PUBKEY_BASE64'
|
||||
export PUBKEY_VALIDITY_PROOF_CONTEXT='$PUBKEY_VALIDITY_PROOF_CONTEXT'
|
||||
export TOKEN_2022_CONFIDENTIAL_ACCOUNT='$TOKEN_2022_CONFIDENTIAL_ACCOUNT'
|
||||
ENV
|
||||
chmod 600 "$FIXTURE_FILE"
|
||||
printf 'Fixture ElGamal Registry validée: %s\n' "$FIXTURE_FILE"
|
||||
printf 'Registry: %s\n' "$ELGAMAL_REGISTRY_ADDRESS"
|
||||
printf 'Proof context: %s\n' "$PUBKEY_VALIDITY_PROOF_CONTEXT"
|
||||
printf 'Token account: %s\n' "$TOKEN_2022_CONFIDENTIAL_ACCOUNT"
|
||||
105
migration/khadhroony-bot2-reference/scripts/devnet/prepare_spl_fixture.sh
Executable file
105
migration/khadhroony-bot2-reference/scripts/devnet/prepare_spl_fixture.sh
Executable file
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env bash
|
||||
# file: scripts/devnet/prepare_spl_fixture.sh
|
||||
# version: 2
|
||||
set -euo pipefail
|
||||
|
||||
family="${1:-token-2022}"
|
||||
force="${2:-}"
|
||||
workspace="${KB_WORKSPACE_ROOT:-$(pwd)}"
|
||||
wallet_dir="${KB_DEVNET_WALLET_DIR:-$workspace/wallets/temporary/local_devnet}"
|
||||
wallet_file="${KB_DEVNET_WALLET_FILE:-$wallet_dir/local-devnet-operator.json}"
|
||||
case "$family" in
|
||||
token-2022) program='TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb'; fixture_dir="$wallet_dir/spl_token_2022_validation" ;;
|
||||
classic) program='TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'; fixture_dir="$wallet_dir/spl_token_classic_validation" ;;
|
||||
*) printf 'Usage: %s token-2022|classic [--force]\n' "$0" >&2; exit 64 ;;
|
||||
esac
|
||||
command -v solana-keygen >/dev/null
|
||||
command -v solana >/dev/null
|
||||
command -v spl-token >/dev/null
|
||||
test -f "$wallet_file"
|
||||
mkdir -p "$fixture_dir"
|
||||
chmod 700 "$fixture_dir"
|
||||
wallet_address="$(solana-keygen pubkey "$wallet_file")"
|
||||
solana config set --url devnet >/dev/null
|
||||
balance="$(solana balance "$wallet_address" --url devnet)"
|
||||
fixture="$fixture_dir/fixture.env"
|
||||
if [[ -f "$fixture" && "$force" != "--force" ]]; then
|
||||
required_prefix='TOKEN_2022'
|
||||
[[ "$family" == classic ]] && required_prefix='TOKEN_CLASSIC'
|
||||
# shellcheck disable=SC1090
|
||||
source "$fixture"
|
||||
complete=true
|
||||
for suffix in PROGRAM MINT SOURCE DESTINATION DELEGATE CLOSE_ACCOUNT ATA AUTHORITY DECIMALS; do
|
||||
variable="${required_prefix}_${suffix}"
|
||||
[[ -n "${!variable:-}" ]] || complete=false
|
||||
done
|
||||
if [[ "$family" == token-2022 ]]; then
|
||||
variable="${required_prefix}_FREEZE_AUTHORITY"
|
||||
[[ -n "${!variable:-}" ]] || complete=false
|
||||
fi
|
||||
if [[ "$complete" == true ]]; then
|
||||
printf 'Fixture existante conservée: %s\n' "$fixture"
|
||||
printf 'Wallet: %s (%s)\n' "$wallet_address" "$balance"
|
||||
exit 0
|
||||
fi
|
||||
printf 'Fixture incomplète: %s\nRelancer avec: %s %s --force\n' "$fixture" "$0" "$family" >&2
|
||||
exit 66
|
||||
fi
|
||||
if [[ "$family" == token-2022 ]]; then
|
||||
if ! spl-token create-token --help 2>&1 | grep -q -- '--enable-freeze'; then
|
||||
printf 'spl-token ne publie pas --enable-freeze; impossible de préparer Freeze/Thaw proprement.
|
||||
' >&2
|
||||
exit 69
|
||||
fi
|
||||
create_output="$(spl-token --url devnet --program-id "$program" create-token --decimals 9 --enable-freeze)"
|
||||
else
|
||||
create_output="$(spl-token --url devnet --program-id "$program" create-token --decimals 9)"
|
||||
fi
|
||||
mint="$(printf '%s\n' "$create_output" | sed -nE 's/^Creating token ([1-9A-HJ-NP-Za-km-z]+).*$/\1/p' | head -n 1)"
|
||||
test -n "$mint"
|
||||
source_keypair="$fixture_dir/source-token-account.json"
|
||||
destination_keypair="$fixture_dir/destination-token-account.json"
|
||||
delegate_keypair="$fixture_dir/delegate.json"
|
||||
close_keypair="$fixture_dir/close-token-account.json"
|
||||
for keypair in "$source_keypair" "$destination_keypair" "$delegate_keypair" "$close_keypair"; do
|
||||
solana-keygen new --no-bip39-passphrase --silent --force --outfile "$keypair" >/dev/null
|
||||
chmod 600 "$keypair"
|
||||
done
|
||||
source_address="$(solana-keygen pubkey "$source_keypair")"
|
||||
destination_address="$(solana-keygen pubkey "$destination_keypair")"
|
||||
delegate_address="$(solana-keygen pubkey "$delegate_keypair")"
|
||||
close_address="$(solana-keygen pubkey "$close_keypair")"
|
||||
spl-token --url devnet --program-id "$program" --fee-payer "$wallet_file" create-account "$mint" "$source_keypair" --owner "$wallet_address" >/dev/null
|
||||
spl-token --url devnet --program-id "$program" --fee-payer "$wallet_file" create-account "$mint" "$destination_keypair" --owner "$wallet_address" >/dev/null
|
||||
spl-token --url devnet --program-id "$program" --fee-payer "$wallet_file" create-account "$mint" "$close_keypair" --owner "$wallet_address" >/dev/null
|
||||
spl-token --url devnet --program-id "$program" --fee-payer "$wallet_file" mint "$mint" 10 "$source_address" >/dev/null
|
||||
ata="$(spl-token --url devnet --program-id "$program" address --verbose --token "$mint" --owner "$wallet_address" | sed -nE 's/^Associated token address:[[:space:]]+//p')"
|
||||
tmp="$fixture.tmp"
|
||||
prefix='TOKEN_2022'
|
||||
freeze_authority="$wallet_address"
|
||||
if [[ "$family" == classic ]]; then
|
||||
prefix='TOKEN_CLASSIC'
|
||||
freeze_authority=''
|
||||
fi
|
||||
cat > "$tmp" <<ENV
|
||||
export KB_DEVNET_WALLET_DIR='$wallet_dir'
|
||||
export KB_DEVNET_WALLET_FILE='$wallet_file'
|
||||
export KB_DEVNET_WALLET_ADDRESS='$wallet_address'
|
||||
export ${prefix}_PROGRAM='$program'
|
||||
export ${prefix}_MINT='$mint'
|
||||
export ${prefix}_SOURCE='$source_address'
|
||||
export ${prefix}_DESTINATION='$destination_address'
|
||||
export ${prefix}_DELEGATE='$delegate_address'
|
||||
export ${prefix}_CLOSE_ACCOUNT='$close_address'
|
||||
export ${prefix}_ATA='$ata'
|
||||
export ${prefix}_AUTHORITY='$wallet_address'
|
||||
export ${prefix}_FREEZE_AUTHORITY='$freeze_authority'
|
||||
export ${prefix}_DECIMALS='9'
|
||||
export ${prefix}_MINT_AMOUNT_RAW='10000000000'
|
||||
export ${prefix}_TRANSFER_AMOUNT_RAW='4'
|
||||
export ${prefix}_APPROVE_AMOUNT_RAW='2'
|
||||
export ${prefix}_BURN_AMOUNT_RAW='1'
|
||||
ENV
|
||||
mv "$tmp" "$fixture"
|
||||
chmod 600 "$fixture"
|
||||
printf 'Fixture créée: %s\nWallet: %s (%s)\nMint: %s\nSource: %s\nDestination: %s\nDelegate: %s\nClose account: %s\nATA: %s\n' "$fixture" "$wallet_address" "$balance" "$mint" "$source_address" "$destination_address" "$delegate_address" "$close_address" "$ata"
|
||||
30
migration/khadhroony-bot2-reference/scripts/run_agave_local_research.sh
Executable file
30
migration/khadhroony-bot2-reference/scripts/run_agave_local_research.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
exec agave-validator \
|
||||
--identity "$ROOT_DIR/data/agave-validator-keypair.json" \
|
||||
--known-validator 7Np41oeYqPefeNQEHSv1UDhYrehxin3NStELsSKCT4K2 \
|
||||
--known-validator GdnSyH3YtwcxFvQrVVJMm1JhTS4QVX7MFsX56uJLUfiZ \
|
||||
--known-validator DE1bawNcRJB9rVm3buyMVfr8mBEoyyu73NBovf2oXJsJ \
|
||||
--known-validator CakcnaRDHka2gXyfbEd2d3xsvkJkqsLw2akB3zsN1D2S \
|
||||
--only-known-rpc \
|
||||
--no-voting \
|
||||
--full-rpc-api \
|
||||
--enable-rpc-transaction-history \
|
||||
--rpc-pubsub-enable-block-subscription \
|
||||
--private-rpc \
|
||||
--ledger "$ROOT_DIR/data/agave-ledger" \
|
||||
--accounts "$ROOT_DIR/data/agave-accounts" \
|
||||
--log "$ROOT_DIR/data/agave-logs/agave-validator.log" \
|
||||
--rpc-port 8899 \
|
||||
--dynamic-port-range 8000-8025 \
|
||||
--entrypoint entrypoint.mainnet-beta.solana.com:8001 \
|
||||
--entrypoint entrypoint2.mainnet-beta.solana.com:8001 \
|
||||
--entrypoint entrypoint3.mainnet-beta.solana.com:8001 \
|
||||
--entrypoint entrypoint4.mainnet-beta.solana.com:8001 \
|
||||
--entrypoint entrypoint5.mainnet-beta.solana.com:8001 \
|
||||
--expected-genesis-hash 5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d \
|
||||
--wal-recovery-mode skip_any_corrupted_record \
|
||||
--limit-ledger-size 50000000
|
||||
Reference in New Issue
Block a user