use std::fs;
use std::io::Read;
use std::path::Path;
use crate::error::{ManifestError, Result};
use super::{ContractManifest, MAX_MANIFEST_SIZE};
fn validate_manifest_strict(manifest: &ContractManifest) -> Result<()> {
if !manifest.features.is_empty() {
return Err(ManifestError::Validation {
message: format!(
"features must be an empty object in Neo N3, got keys: {}",
manifest
.features
.keys()
.map(String::as_str)
.collect::<Vec<_>>()
.join(", ")
),
}
.into());
}
for (index, permission) in manifest.permissions.iter().enumerate() {
if let super::ManifestPermissionContract::Other(value) = &permission.contract {
return Err(ManifestError::Validation {
message: format!(
"permissions[{index}].contract must be \"*\", a 0x-prefixed 20-byte \
contract hash, or a 33-byte group public key, got {value}"
),
}
.into());
}
if let super::ManifestPermissionMethods::Wildcard(value) = &permission.methods {
if value != "*" {
return Err(ManifestError::Validation {
message: format!(
"permissions[{index}].methods wildcard must be \"*\", got {value:?}"
),
}
.into());
}
}
}
if let Some(super::ManifestTrusts::Wildcard(value)) = manifest.trusts.as_ref() {
if value != "*" {
return Err(ManifestError::Validation {
message: format!("trusts wildcard must be \"*\", got {value:?}"),
}
.into());
}
}
Ok(())
}
fn ensure_manifest_size(size: u64) -> Result<()> {
if size > MAX_MANIFEST_SIZE {
return Err(ManifestError::FileTooLarge {
size,
max: MAX_MANIFEST_SIZE,
}
.into());
}
Ok(())
}
impl ContractManifest {
pub fn from_reader<R: Read>(reader: R) -> Result<Self> {
let mut buf = Vec::new();
let mut limited = reader.take(MAX_MANIFEST_SIZE + 1);
limited.read_to_end(&mut buf).map_err(ManifestError::from)?;
Self::from_bytes(&buf)
}
pub fn from_json_str(input: &str) -> Result<Self> {
input.parse()
}
pub fn from_json_str_strict(input: &str) -> Result<Self> {
let manifest = Self::from_json_str(input)?;
validate_manifest_strict(&manifest)?;
Ok(manifest)
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
ensure_manifest_size(bytes.len() as u64)?;
let text =
std::str::from_utf8(bytes).map_err(|err| ManifestError::InvalidUtf8 { source: err })?;
Self::from_json_str(text)
}
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let size = fs::metadata(&path)?.len();
ensure_manifest_size(size)?;
let data = fs::read(path)?;
Self::from_bytes(&data)
}
pub fn from_file_strict<P: AsRef<Path>>(path: P) -> Result<Self> {
let size = fs::metadata(&path)?.len();
ensure_manifest_size(size)?;
let data = fs::read(path)?;
let manifest = Self::from_bytes(&data)?;
validate_manifest_strict(&manifest)?;
Ok(manifest)
}
}
impl std::str::FromStr for ContractManifest {
type Err = crate::error::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let manifest: ContractManifest = serde_json::from_str(s).map_err(ManifestError::from)?;
Ok(manifest)
}
}