Unit
CLI generate subcommand + metaboard emit tx-data (crates/cli/src/metaboard.rs, crates/cli/src/meta/types/dotrain/source_v1.rs) — AMT group g5-interpretercallermeta-v1.
Intent oracle
DotrainSourceEmitData.subject doc comment (crates/cli/src/metaboard.rs): "Keccak256 hash of the content (hex-encoded with 0x prefix)".
DotrainSourceV1::hash() doc comment (crates/cli/src/meta/types/dotrain/source_v1.rs): "Returns the hash of the dotrain source code" — implemented as keccak256(self.0.as_bytes()), i.e. hash of the raw content.
IMetaV1_2 NatSpec (src/interface/unstable/IMetaV1_2.sol): subject "will often be a hash of some data/thing that this metadata is about" — interpretation is context specific, so the crate's own convention is the oracle for the dotrain-source context.
Observed behaviour
generate_dotrain_source_emit_tx_data derives subject as document.hash(false) = keccak256 of the CBOR-encoded meta item map (payload + magic + contentType + ...), NOT keccak256 of the raw source content.
So the same crate exposes two different subject derivations for the same dotrain source:
- emit half (
rain-metadata generate source / generate_dotrain_source_emit_tx_data): keccak256(cbor_encode(item))
- fetch half (
DotrainSourceV1::hash() + DotrainSourceV1::fetch_by_subject(subject, url)): the only hash the type itself offers is keccak256(content)
A consumer that computes DotrainSourceV1::hash(&source) and calls fetch_by_subject with it will never find a meta published via the generate flow, because that meta was emitted under the item-map hash.
Violated property
Content-addressable round-trip within one crate: subject(emit) == subject(fetch key) for the same source content; and doc/impl agreement for DotrainSourceEmitData.subject.
Verified repro
let content = "#main _ _: int-add(1 2) int-add(2 3)";
let tx = generate_dotrain_source_emit_tx_data(content).unwrap();
let content_hash = alloy::primitives::hex::encode_prefixed(DotrainSourceV1(content.to_string()).hash());
assert_ne!(tx.subject, content_hash); // passes today: the two subjects differ
Concrete values for content = "#main _ _: int-add(1 2) int-add(2 3)" (from the built CLI at bba50a7):
- generate flow subject (keccak of cbor item map):
0xaeb47b040e0c281065bdaed6a3c81284c6ad0be63eed471b3b75dd5672c1c68e
DotrainSourceV1::hash / keccak256 of the raw content: 0x55d083bf6ee7d68b9f5f9138c1f0c7d42a906fd079efe2825fe6a4d80329e104
A fetch_by_subject(0x55d0…) lookup can never find the meta emitted under 0xaeb4….
Triage framing (neutral)
Either derivation may be the intended convention:
- If "subject = keccak256 of the raw content" (as the field doc and
DotrainSourceV1::hash say) is intended, the generate flow emits under the wrong subject and DotrainSourceEmitData.subject's value is mislabeled.
- If "subject = keccak256 of the CBOR item map" (
document.hash(false)) is intended, then the DotrainSourceEmitData.subject doc comment is wrong and DotrainSourceV1::hash() is a footgun as the natural-looking key for fetch_by_subject.
Not adjudicating which; flagging the inconsistency for triage. Existing tests only pin internal consistency (subject-in-calldata == subject field), not the cross-API convention.
Unit
CLI generate subcommand + metaboard emit tx-data (
crates/cli/src/metaboard.rs,crates/cli/src/meta/types/dotrain/source_v1.rs) — AMT group g5-interpretercallermeta-v1.Intent oracle
DotrainSourceEmitData.subjectdoc comment (crates/cli/src/metaboard.rs): "Keccak256 hash of the content (hex-encoded with 0x prefix)".DotrainSourceV1::hash()doc comment (crates/cli/src/meta/types/dotrain/source_v1.rs): "Returns the hash of the dotrain source code" — implemented askeccak256(self.0.as_bytes()), i.e. hash of the raw content.IMetaV1_2NatSpec (src/interface/unstable/IMetaV1_2.sol): subject "will often be a hash of some data/thing that this metadata is about" — interpretation is context specific, so the crate's own convention is the oracle for the dotrain-source context.Observed behaviour
generate_dotrain_source_emit_tx_dataderivessubjectasdocument.hash(false)= keccak256 of the CBOR-encoded meta item map (payload + magic + contentType + ...), NOT keccak256 of the raw source content.So the same crate exposes two different subject derivations for the same dotrain source:
rain-metadata generate source/generate_dotrain_source_emit_tx_data):keccak256(cbor_encode(item))DotrainSourceV1::hash()+DotrainSourceV1::fetch_by_subject(subject, url)): the only hash the type itself offers iskeccak256(content)A consumer that computes
DotrainSourceV1::hash(&source)and callsfetch_by_subjectwith it will never find a meta published via the generate flow, because that meta was emitted under the item-map hash.Violated property
Content-addressable round-trip within one crate: subject(emit) == subject(fetch key) for the same source content; and doc/impl agreement for
DotrainSourceEmitData.subject.Verified repro
Concrete values for
content = "#main _ _: int-add(1 2) int-add(2 3)"(from the built CLI at bba50a7):0xaeb47b040e0c281065bdaed6a3c81284c6ad0be63eed471b3b75dd5672c1c68eDotrainSourceV1::hash/ keccak256 of the raw content:0x55d083bf6ee7d68b9f5f9138c1f0c7d42a906fd079efe2825fe6a4d80329e104A
fetch_by_subject(0x55d0…)lookup can never find the meta emitted under0xaeb4….Triage framing (neutral)
Either derivation may be the intended convention:
DotrainSourceV1::hashsay) is intended, the generate flow emits under the wrong subject andDotrainSourceEmitData.subject's value is mislabeled.document.hash(false)) is intended, then theDotrainSourceEmitData.subjectdoc comment is wrong andDotrainSourceV1::hash()is a footgun as the natural-looking key forfetch_by_subject.Not adjudicating which; flagging the inconsistency for triage. Existing tests only pin internal consistency (subject-in-calldata == subject field), not the cross-API convention.