40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
# File 'lib/igniter_lang/compiler_profile_contract_validator.rb', line 40
def self.validate(contract, digest_reference_policy: DEFAULT_DIGEST_REFERENCE_POLICY)
policy = digest_reference_policy.to_s
diagnostics = []
unless contract.is_a?(Hash)
diagnostics << diagnostic("wrong_kind", "expected compiler_profile_contract", "kind")
return result(diagnostics, policy)
end
diagnostics << diagnostic("wrong_kind", "expected compiler_profile_contract", "kind") unless contract["kind"] == "compiler_profile_contract"
diagnostics << diagnostic("unsupported_format_version", "expected format_version 0.1.0", "format_version") unless contract["format_version"] == FORMAT_VERSION
diagnostics << diagnostic("descriptor_digest_invalid", "descriptor_digest must be compiler_profile_descriptor/sha256:<hex>", "descriptor_digest") unless contract["descriptor_digest"].to_s.match?(DESCRIPTOR_DIGEST_PATTERN)
diagnostics << diagnostic("finalization_payload_digest_invalid", "finalization_payload_digest must be sha256:<64 hex>", "finalization_payload_digest") unless contract["finalization_payload_digest"].to_s.match?(FINALIZATION_PAYLOAD_DIGEST_PATTERN)
contract_digest_recomputable = validate_contract_digest_shape(diagnostics, contract, policy)
slot_order = Array(contract["slot_order"])
slot_assignments = contract["slot_assignments"] || {}
Array(contract.dig("required_slot_schema", "required_slots")).each do |slot|
unless slot_order.include?(slot) && slot_assignments.key?(slot)
diagnostics << diagnostic("missing_required_slot", "required slot #{slot.inspect} is missing from slot_order or slot_assignments", "slot_assignments.#{slot}")
end
end
strict_registries = contract["strict_registries"] || {}
strict_registries.each do |registry_name, entries|
seen = {}
Array(entries).each do |entry|
key = entry["key"]
if seen.key?(key)
diagnostics << diagnostic("duplicate_strict_key", "strict registry #{registry_name} has duplicate key #{key.inspect}", "strict_registries.#{registry_name}.#{key}")
end
seen[key] = true
end
end
rules = Array(contract.dig("ordered_rule_graph", "rules"))
rule_ids = rules.map { |rule| rule["rule_id"] }
rules.each do |rule|
(Array(rule["before"]) + Array(rule["after"])).each do |ref|
unless rule_ids.include?(ref)
diagnostics << diagnostic("missing_rule_reference", "ordered rule #{rule["rule_id"]} references missing rule #{ref.inspect}", "ordered_rule_graph.rules.#{rule["rule_id"]}")
end
end
end
cycle = find_rule_cycle(rules)
diagnostics << diagnostic("rule_cycle", "ordered rule graph contains cycle: #{cycle.join(" -> ")}", "ordered_rule_graph.rules") if cycle
non_authority = contract["non_authority"] || {}
diagnostics << diagnostic("runtime_authority_forbidden", "compiler profile contract cannot grant runtime authority", "non_authority.runtime_authority_granted") if non_authority["runtime_authority_granted"]
diagnostics << diagnostic("dispatch_migration_forbidden", "compiler profile contract cannot authorize dispatch migration", "non_authority.dispatch_migration_authorized") if non_authority["dispatch_migration_authorized"]
validate_contract_digest_match(diagnostics, contract) if contract_digest_recomputable
result(diagnostics, policy)
end
|