Fix Plutus v3 validator hash calculation in blueprint.

This commit is contained in:
KtorZ
2024-08-08 18:17:38 +02:00
parent 445ffc483d
commit 508d88035b
6 changed files with 144 additions and 43 deletions

View File

@@ -111,38 +111,99 @@ where
}
}
impl Serialize for Program<DeBruijn> {
#[derive(Debug, Clone, PartialEq)]
pub enum SerializableProgram {
PlutusV1Program(Program<DeBruijn>),
PlutusV2Program(Program<DeBruijn>),
PlutusV3Program(Program<DeBruijn>),
}
impl SerializableProgram {
pub fn inner(&self) -> &Program<DeBruijn> {
use SerializableProgram::*;
match self {
PlutusV1Program(program) => program,
PlutusV2Program(program) => program,
PlutusV3Program(program) => program,
}
}
pub fn map<F>(self, f: F) -> Self
where
F: FnOnce(Program<DeBruijn>) -> Program<DeBruijn>,
{
use SerializableProgram::*;
match self {
PlutusV1Program(program) => PlutusV1Program(f(program)),
PlutusV2Program(program) => PlutusV2Program(f(program)),
PlutusV3Program(program) => PlutusV3Program(f(program)),
}
}
pub fn compiled_code_and_hash(&self) -> (String, pallas_crypto::hash::Hash<28>) {
use SerializableProgram::*;
match self {
PlutusV1Program(pgrm) => {
let cbor = pgrm.to_cbor().unwrap();
let compiled_code = hex::encode(&cbor);
let hash = conway::PlutusV1Script(cbor.into()).compute_hash();
(compiled_code, hash)
}
PlutusV2Program(pgrm) => {
let cbor = pgrm.to_cbor().unwrap();
let compiled_code = hex::encode(&cbor);
let hash = conway::PlutusV2Script(cbor.into()).compute_hash();
(compiled_code, hash)
}
PlutusV3Program(pgrm) => {
let cbor = pgrm.to_cbor().unwrap();
let compiled_code = hex::encode(&cbor);
let hash = conway::PlutusV3Script(cbor.into()).compute_hash();
(compiled_code, hash)
}
}
}
}
impl Serialize for SerializableProgram {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let cbor = self.to_cbor().unwrap();
let (compiled_code, hash) = self.compiled_code_and_hash();
let mut s = serializer.serialize_struct("Program<DeBruijn>", 2)?;
s.serialize_field("compiledCode", &hex::encode(&cbor))?;
s.serialize_field("hash", &conway::PlutusV2Script(cbor.into()).compute_hash())?;
s.serialize_field("compiledCode", &compiled_code)?;
s.serialize_field("hash", &hash)?;
s.end()
}
}
impl<'a> Deserialize<'a> for Program<DeBruijn> {
impl<'a> Deserialize<'a> for SerializableProgram {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(serde::Deserialize)]
#[serde(field_identifier, rename_all = "camelCase")]
enum Fields {
CompiledCode,
Hash,
}
struct ProgramVisitor;
impl<'a> Visitor<'a> for ProgramVisitor {
type Value = Program<DeBruijn>;
type Value = SerializableProgram;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Program<Visitor>")
formatter.write_str("validator")
}
fn visit_map<V>(self, mut map: V) -> Result<Program<DeBruijn>, V::Error>
fn visit_map<V>(self, mut map: V) -> Result<SerializableProgram, V::Error>
where
V: MapAccess<'a>,
{
let mut compiled_code: Option<String> = None;
let mut hash: Option<String> = None;
while let Some(key) = map.next_key()? {
match key {
Fields::CompiledCode => {
@@ -151,11 +212,20 @@ impl<'a> Deserialize<'a> for Program<DeBruijn> {
}
compiled_code = Some(map.next_value()?);
}
Fields::Hash => {
if hash.is_some() {
return Err(de::Error::duplicate_field("hash"));
}
hash = Some(map.next_value()?);
}
}
}
let compiled_code =
compiled_code.ok_or_else(|| de::Error::missing_field("compiledCode"))?;
let hash = hash.ok_or_else(|| de::Error::missing_field("hash"))?;
let mut cbor_buffer = Vec::new();
let mut flat_buffer = Vec::new();
@@ -166,10 +236,29 @@ impl<'a> Deserialize<'a> for Program<DeBruijn> {
&"a base16-encoded CBOR-serialized UPLC program",
)
})
.and_then(|program| {
let cbor = || program.to_cbor().unwrap().into();
if conway::PlutusV3Script(cbor()).compute_hash().to_string() == hash {
return Ok(SerializableProgram::PlutusV3Program(program));
}
if conway::PlutusV2Script(cbor()).compute_hash().to_string() == hash {
return Ok(SerializableProgram::PlutusV2Program(program));
}
if conway::PlutusV1Script(cbor()).compute_hash().to_string() == hash {
return Ok(SerializableProgram::PlutusV1Program(program));
}
Err(de::Error::custom(
"hash doesn't match any recognisable Plutus version.",
))
})
}
}
const FIELDS: &[&str] = &["compiledCode"];
const FIELDS: &[&str] = &["compiledCode", "hash"];
deserializer.deserialize_struct("Program<DeBruijn>", FIELDS, ProgramVisitor)
}
}