I have function that will take an admin account and verify when metadata is passed in that the admin account has signed it and the Signature is provided in a struct called NftSaleMetadata<AccountId, BoundedString>
. When I write unit tests the tests pass, but when I write the javascript scripts to sign the metadata then execute a transaction, I get failures. Is there something wrong with my verification function?
NftSaleMetadata
struct
/// Metadata to ensure metadata passed in is signed by the Overlord account
#[derive(Encode, Decode, Clone, Debug, PartialEq, TypeInfo)]
pub struct NftSaleMetadata<BoundedString> {
pub metadata: BoundedString,
pub signature: sr25519::Signature,
}
verify_nft_metadata
that is called from a public minting function
pub fn verify_nft_metadata(
overlord: &T::AccountId,
nft_metadata: NftSaleMetadata<BoundedVec<u8, T::StringLimit>>,
) -> Result<BoundedVec<u8, T::StringLimit>, Error<T>> {
let metadata = nft_metadata.metadata;
let signature = nft_metadata.signature;
// Serialize evidence
let msg = Encode::encode(&metadata);
let encode_overlord = T::AccountId::encode(overlord);
let h256_overlord = H256::from_slice(&encode_overlord);
let overlord_key = sr25519::Public::from_h256(h256_overlord);
// verify claim
ensure!(
sp_io::crypto::sr25519_verify(&signature, &msg, &overlord_key),
Error::<T>::InvalidMetadata
);
Ok(metadata)
}
Unit test example that passes where claim_spirit
will call helper function verify_nft_metadata
to verify the signed metadata
#[test]
fn claimed_spirit_works() {
ExtBuilder::default().build(OVERLORD).execute_with(|| {
let overlord_pair = sr25519::Pair::from_seed(b"28133080042813308004281330800428");
// let overlord_pub = overlord_pair.public();
// Set Overlord and configuration then enable spirits to be claimed
setup_config(StatusType::ClaimSpirits);
let metadata = stb("I am Spirit");
let enc_metadata = Encode::encode(&metadata);
let metadata_signature = overlord_pair.sign(&enc_metadata);
let nft_sale_metadata =
NftSaleMetadata { metadata: metadata.clone(), signature: metadata_signature };
// Sign BOB's Public Key and Metadata encoding with OVERLORD account
let claim = Encode::encode(&BOB);
let overlord_signature = overlord_pair.sign(&claim);
let bob_ticket = ClaimSpiritTicket { account: BOB, signature: overlord_signature };
// Dispatch a claim spirit from BOB's account
assert_ok!(PhalaWorld::claim_spirit(
Origin::signed(BOB),
Some(bob_ticket),
nft_sale_metadata.clone()
));
// ALICE should be able to claim since she has minimum amount of PHA
assert_ok!(PhalaWorld::claim_spirit(Origin::signed(ALICE), None, nft_sale_metadata));
});
}
JS script that fail when trying to claim_spirit
the output to console says valid signature, but the transaction fails with invalid metadata
const keyring = new Keyring({type: 'sr25519'});
// status types
const claimSpirits = api.createType('StatusType', 'ClaimSpirits');
const overlord = keyring.addFromUri(overlordPrivkey);
const metadata = stringToU8a('I am Spirit');
const metadataSig = overlord.sign(metadata);
const isValid = overlord.verify(metadata, metadataSig, overlord.publicKey);
// output the result
console.log(`${u8aToHex(metadataSig)}\n ${u8aToHex(metadata)} is ${isValid ? 'valid' : 'invalid'}`);
const nftSignedMetadata = api.createType("NftSaleMetadata", {'metadata': metadata, 'signature': metadataSig});
await api.tx.phalaWorld.claimSpirit(null, nftSignedMetadata).signAndSend(user);