rename "nonce" to "fingerprint"

This commit is contained in:
Andrew Kelley
2025-02-26 04:01:28 -08:00
parent 67904e925d
commit de43f5eb6a
7 changed files with 38 additions and 38 deletions

View File

@ -12,5 +12,5 @@
},
},
.paths = .{""},
.nonce = 0xc1ce108124179e16,
.fingerprint = 0xc1ce108124179e16,
}

View File

@ -22,24 +22,24 @@ Zig package namespace.
Must be a valid bare Zig identifier (don't `@` me), limited to 32 bytes.
Together with `nonce`, this represents a globally unique package identifier.
Together with `fingerprint`, this represents a globally unique package identifier.
### `nonce`
### `fingerprint`
Together with `name`, this represents a globally unique package identifier. This
field is auto-initialized by the toolchain when the package is first created,
and then *never changes*. This allows Zig to unambiguously detect when one
package is an updated version of another.
When forking a Zig project, this nonce should be regenerated if the upstream
When forking a Zig project, this fingerprint should be regenerated if the upstream
project is still maintained. Otherwise, the fork is *hostile*, attempting to
take control over the original project's identity. The nonce can be regenerated
take control over the original project's identity. The fingerprint can be regenerated
by deleting the field and running `zig build`.
This 64-bit integer is the combination of a 32-bit id component and a 32-bit
checksum.
The id component within the nonce has these restrictions:
The id component within the fingerprint has these restrictions:
`0x00000000` is reserved for legacy packages.

View File

@ -24,7 +24,7 @@
// original project's identity. Thus it is recommended to leave the comment
// on the following line intact, so that it shows up in code reviews that
// modify the field.
.nonce = .NONCE, // Changing this has security and trust implications.
.fingerprint = .FINGERPRINT, // Changing this has security and trust implications.
// Tracks the earliest Zig version that the package considers to be a
// supported use case.

View File

@ -10,25 +10,25 @@ pub const multihash_len = 1 + 1 + Hash.Algo.digest_length;
pub const multihash_hex_digest_len = 2 * multihash_len;
pub const MultiHashHexDigest = [multihash_hex_digest_len]u8;
pub const Nonce = packed struct(u64) {
pub const Fingerprint = packed struct(u64) {
id: u32,
checksum: u32,
pub fn generate(name: []const u8) Nonce {
pub fn generate(name: []const u8) Fingerprint {
return .{
.id = std.crypto.random.intRangeLessThan(u32, 1, 0xffffffff),
.checksum = std.hash.Crc32.hash(name),
};
}
pub fn validate(n: Nonce, name: []const u8) bool {
pub fn validate(n: Fingerprint, name: []const u8) bool {
switch (n.id) {
0x00000000, 0xffffffff => return false,
else => return std.hash.Crc32.hash(name) == n.checksum,
}
}
pub fn int(n: Nonce) u64 {
pub fn int(n: Fingerprint) u64 {
return @bitCast(n);
}
};

View File

@ -44,7 +44,7 @@ omit_missing_hash_error: bool,
/// which specifies inclusion rules. This is intended to be true for the first
/// fetch task and false for the recursive dependencies.
allow_missing_paths_field: bool,
allow_missing_nonce: bool,
allow_missing_fingerprint: bool,
allow_name_string: bool,
/// If true and URL points to a Git repository, will use the latest commit.
use_latest_commit: bool,
@ -649,7 +649,7 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void {
f.manifest = try Manifest.parse(arena, ast.*, .{
.allow_missing_paths_field = f.allow_missing_paths_field,
.allow_missing_nonce = f.allow_missing_nonce,
.allow_missing_fingerprint = f.allow_missing_fingerprint,
.allow_name_string = f.allow_name_string,
});
const manifest = &f.manifest.?;
@ -752,7 +752,7 @@ fn queueJobsForDeps(f: *Fetch) RunError!void {
.job_queue = f.job_queue,
.omit_missing_hash_error = false,
.allow_missing_paths_field = true,
.allow_missing_nonce = true,
.allow_missing_fingerprint = true,
.allow_name_string = true,
.use_latest_commit = false,
@ -2323,7 +2323,7 @@ const TestFetchBuilder = struct {
.job_queue = &self.job_queue,
.omit_missing_hash_error = true,
.allow_missing_paths_field = false,
.allow_missing_nonce = true, // so we can keep using the old testdata .tar.gz
.allow_missing_fingerprint = true, // so we can keep using the old testdata .tar.gz
.allow_name_string = true, // so we can keep using the old testdata .tar.gz
.use_latest_commit = true,

View File

@ -52,7 +52,7 @@ pub const ParseOptions = struct {
/// Deprecated, to be removed after 0.14.0 is tagged.
allow_name_string: bool = true,
/// Deprecated, to be removed after 0.14.0 is tagged.
allow_missing_nonce: bool = true,
allow_missing_fingerprint: bool = true,
};
pub const Error = Allocator.Error;
@ -81,7 +81,7 @@ pub fn parse(gpa: Allocator, ast: Ast, options: ParseOptions) Error!Manifest {
.paths = .{},
.allow_missing_paths_field = options.allow_missing_paths_field,
.allow_name_string = options.allow_name_string,
.allow_missing_nonce = options.allow_missing_nonce,
.allow_missing_fingerprint = options.allow_missing_fingerprint,
.minimum_zig_version = null,
.buf = .{},
};
@ -157,7 +157,7 @@ const Parse = struct {
paths: std.StringArrayHashMapUnmanaged(void),
allow_missing_paths_field: bool,
allow_name_string: bool,
allow_missing_nonce: bool,
allow_missing_fingerprint: bool,
minimum_zig_version: ?std.SemanticVersion,
const InnerError = error{ ParseFailure, OutOfMemory };
@ -175,7 +175,7 @@ const Parse = struct {
var have_name = false;
var have_version = false;
var have_included_paths = false;
var nonce: ?Package.Nonce = null;
var fingerprint: ?Package.Fingerprint = null;
for (struct_init.ast.fields) |field_init| {
const name_token = ast.firstToken(field_init) - 2;
@ -192,8 +192,8 @@ const Parse = struct {
} else if (mem.eql(u8, field_name, "name")) {
p.name = try parseName(p, field_init);
have_name = true;
} else if (mem.eql(u8, field_name, "nonce")) {
nonce = try parseNonce(p, field_init);
} else if (mem.eql(u8, field_name, "fingerprint")) {
fingerprint = try parseFingerprint(p, field_init);
} else if (mem.eql(u8, field_name, "version")) {
p.version_node = field_init;
const version_text = try parseString(p, field_init);
@ -220,16 +220,16 @@ const Parse = struct {
if (!have_name) {
try appendError(p, main_token, "missing top-level 'name' field", .{});
} else {
if (nonce) |n| {
if (fingerprint) |n| {
if (!n.validate(p.name)) {
return fail(p, main_token, "invalid nonce: 0x{x}; if this is a new or forked package, use this value: 0x{x}", .{
n.int(), Package.Nonce.generate(p.name).int(),
return fail(p, main_token, "invalid fingerprint: 0x{x}; if this is a new or forked package, use this value: 0x{x}", .{
n.int(), Package.Fingerprint.generate(p.name).int(),
});
}
p.id = n.id;
} else if (!p.allow_missing_nonce) {
try appendError(p, main_token, "missing top-level 'nonce' field; suggested value: 0x{x}", .{
Package.Nonce.generate(p.name).int(),
} else if (!p.allow_missing_fingerprint) {
try appendError(p, main_token, "missing top-level 'fingerprint' field; suggested value: 0x{x}", .{
Package.Fingerprint.generate(p.name).int(),
});
} else {
p.id = 0;
@ -385,7 +385,7 @@ const Parse = struct {
}
}
fn parseNonce(p: *Parse, node: Ast.Node.Index) !Package.Nonce {
fn parseFingerprint(p: *Parse, node: Ast.Node.Index) !Package.Fingerprint {
const ast = p.ast;
const node_tags = ast.nodes.items(.tag);
const main_tokens = ast.nodes.items(.main_token);

View File

@ -4752,10 +4752,10 @@ fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
};
var ok_count: usize = 0;
const nonce: Package.Nonce = .generate(sanitized_root_name);
const fingerprint: Package.Fingerprint = .generate(sanitized_root_name);
for (template_paths) |template_path| {
if (templates.write(arena, fs.cwd(), sanitized_root_name, template_path, nonce)) |_| {
if (templates.write(arena, fs.cwd(), sanitized_root_name, template_path, fingerprint)) |_| {
std.log.info("created {s}", .{template_path});
ok_count += 1;
} else |err| switch (err) {
@ -5225,7 +5225,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
.job_queue = &job_queue,
.omit_missing_hash_error = true,
.allow_missing_paths_field = false,
.allow_missing_nonce = false,
.allow_missing_fingerprint = false,
.allow_name_string = false,
.use_latest_commit = false,
@ -7127,7 +7127,7 @@ fn cmdFetch(
.job_queue = &job_queue,
.omit_missing_hash_error = true,
.allow_missing_paths_field = false,
.allow_missing_nonce = true,
.allow_missing_fingerprint = true,
.allow_name_string = true,
.use_latest_commit = true,
@ -7468,10 +7468,10 @@ fn loadManifest(
0,
) catch |err| switch (err) {
error.FileNotFound => {
const nonce: Package.Nonce = .generate(options.root_name);
const fingerprint: Package.Fingerprint = .generate(options.root_name);
var templates = findTemplates(gpa, arena);
defer templates.deinit();
templates.write(arena, options.dir, options.root_name, Package.Manifest.basename, nonce) catch |e| {
templates.write(arena, options.dir, options.root_name, Package.Manifest.basename, fingerprint) catch |e| {
fatal("unable to write {s}: {s}", .{
Package.Manifest.basename, @errorName(e),
});
@ -7529,7 +7529,7 @@ const Templates = struct {
out_dir: fs.Dir,
root_name: []const u8,
template_path: []const u8,
nonce: Package.Nonce,
fingerprint: Package.Fingerprint,
) !void {
if (fs.path.dirname(template_path)) |dirname| {
out_dir.makePath(dirname) catch |err| {
@ -7555,9 +7555,9 @@ const Templates = struct {
try templates.buffer.appendSlice(root_name);
i += ".NAME".len;
continue;
} else if (std.mem.startsWith(u8, contents[i..], ".NONCE")) {
try templates.buffer.writer().print("0x{x}", .{nonce.int()});
i += ".NONCE".len;
} else if (std.mem.startsWith(u8, contents[i..], ".FINGERPRINT")) {
try templates.buffer.writer().print("0x{x}", .{fingerprint.int()});
i += ".FINGERPRINT".len;
continue;
} else if (std.mem.startsWith(u8, contents[i..], ".ZIGVER")) {
try templates.buffer.appendSlice(build_options.version);