stellar-strkey 0.0.18

Encode and decode strkeys for Stellar.
Documentation
#![cfg(feature = "serde-decoded")]

use stellar_strkey::{ed25519, *};

#[test]
fn test_ed25519_public_key() {
    assert_eq!(
        serde_json::to_string_pretty(&Decoded(&Strkey::PublicKeyEd25519(ed25519::PublicKey([
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
        ]))))
        .unwrap(),
        r#"{
  "public_key_ed25519": "0000000000000000000000000000000000000000000000000000000000000000"
}"#,
    );
}

#[test]
fn test_ed25519_private_key() {
    let pk = ed25519::PrivateKey([0x00; 32]);
    assert_eq!(
        serde_json::to_string_pretty(&serde_json::json!({
            "private_key_ed25519": Decoded(Unredacted(&pk)),
        }))
        .unwrap(),
        r#"{
  "private_key_ed25519": "0000000000000000000000000000000000000000000000000000000000000000"
}"#,
    );
}

#[test]
fn test_contract() {
    assert_eq!(
        serde_json::to_string_pretty(&Decoded(&Strkey::Contract(Contract([
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
        ]))))
        .unwrap(),
        r#"{
  "contract": "0000000000000000000000000000000000000000000000000000000000000000"
}"#,
    );
}

#[test]
fn test_hash_x() {
    assert_eq!(
        serde_json::to_string_pretty(&Decoded(&Strkey::HashX(HashX([
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
        ]))))
        .unwrap(),
        r#"{
  "hash_x": "0000000000000000000000000000000000000000000000000000000000000000"
}"#,
    );
}

#[test]
fn test_pre_auth_tx() {
    assert_eq!(
        serde_json::to_string_pretty(&Decoded(&Strkey::PreAuthTx(PreAuthTx([
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00,
        ]))))
        .unwrap(),
        r#"{
  "pre_auth_tx": "0000000000000000000000000000000000000000000000000000000000000000"
}"#,
    );
}

#[test]
fn test_ed25519_muxed_account() {
    assert_eq!(
        serde_json::to_string_pretty(&Decoded(&Strkey::MuxedAccountEd25519(
            ed25519::MuxedAccount {
                ed25519: [
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                ],
                id: 0,
            }
        )))
        .unwrap(),
        r#"{
  "muxed_account_ed25519": {
    "ed25519": "0000000000000000000000000000000000000000000000000000000000000000",
    "id": 0
  }
}"#,
    );
}

#[test]
fn test_ed25519_signed_payload() {
    assert_eq!(
        serde_json::to_string_pretty(&Decoded(&Strkey::SignedPayloadEd25519(
            ed25519::SignedPayload::new(
                [
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                ],
                &[1u8, 2, 3, 4],
            )
            .unwrap()
        )))
        .unwrap(),
        r#"{
  "signed_payload_ed25519": {
    "ed25519": "0000000000000000000000000000000000000000000000000000000000000000",
    "payload": "01020304"
  }
}"#,
    );
}

// Regression tests for deserializing `Decoded<Strkey>` from deserializers that
// yield owned (rather than borrowed) string keys. `serde_json::from_str`
// borrows the variant key directly from the input buffer, but `from_value` and
// `from_reader` cannot, so the map visitor must accept an owned key. The
// `encode` CLI is one consumer that hits this, via `from_value`. See issue
// #124.

#[test]
fn test_decode_strkey_from_value() {
    let value = serde_json::json!({
        "public_key_ed25519": "3330317ec241d79943bb9aa7c8ea5f8b89f9c6ea351fe03f8ec3d1127137d484",
    });
    let Decoded(strkey): Decoded<Strkey> = serde_json::from_value(value).unwrap();
    assert!(matches!(strkey, Strkey::PublicKeyEd25519(_)));
}

#[test]
fn test_decode_strkey_from_reader() {
    // `from_reader` consumes a stream and cannot borrow keys from the input.
    let json = r#"{"public_key_ed25519":"3330317ec241d79943bb9aa7c8ea5f8b89f9c6ea351fe03f8ec3d1127137d484"}"#;
    let Decoded(strkey): Decoded<Strkey> = serde_json::from_reader(json.as_bytes()).unwrap();
    assert!(matches!(strkey, Strkey::PublicKeyEd25519(_)));
}

#[test]
fn test_decode_strkey_from_value_nested_variant() {
    // A compound variant (nested object) exercised through the owned-key path.
    let value = serde_json::json!({
        "signed_payload_ed25519": {
            "ed25519": "0000000000000000000000000000000000000000000000000000000000000000",
            "payload": "01020304",
        }
    });
    let Decoded(strkey): Decoded<Strkey> = serde_json::from_value(value).unwrap();
    assert!(matches!(strkey, Strkey::SignedPayloadEd25519(_)));
}

#[test]
fn test_roundtrip_public_key_via_value() {
    // Serialize to a `serde_json::Value`, then deserialize back via `from_value`.
    let original = Strkey::PublicKeyEd25519(ed25519::PublicKey([7u8; 32]));
    let value = serde_json::to_value(Decoded(&original)).unwrap();
    let Decoded(deserialized): Decoded<Strkey> = serde_json::from_value(value).unwrap();
    assert_eq!(original, deserialized);
}

#[test]
fn test_roundtrip_muxed_account() {
    let original = Strkey::MuxedAccountEd25519(ed25519::MuxedAccount {
        ed25519: [0x00; 32],
        id: 42,
    });
    let json = serde_json::to_string(&Decoded(&original)).unwrap();
    let Decoded(deserialized): Decoded<Strkey> = serde_json::from_str(&json).unwrap();
    assert_eq!(original, deserialized);
}

#[test]
fn test_roundtrip_signed_payload() {
    let original = Strkey::SignedPayloadEd25519(
        ed25519::SignedPayload::new([0x00; 32], &[1u8, 2, 3, 4]).unwrap(),
    );
    let json = serde_json::to_string(&Decoded(&original)).unwrap();
    let Decoded(deserialized): Decoded<Strkey> = serde_json::from_str(&json).unwrap();
    assert_eq!(original, deserialized);
}

#[test]
fn test_roundtrip_claimable_balance() {
    let original = Strkey::ClaimableBalance(ClaimableBalance::V0([0x00; 32]));
    let json = serde_json::to_string(&Decoded(&original)).unwrap();
    let Decoded(deserialized): Decoded<Strkey> = serde_json::from_str(&json).unwrap();
    assert_eq!(original, deserialized);
}

#[test]
fn test_extra_variant_keys_rejected() {
    let json = r#"{
        "public_key_ed25519": "0000000000000000000000000000000000000000000000000000000000000000",
        "contract": "0000000000000000000000000000000000000000000000000000000000000000"
    }"#;
    let err = match serde_json::from_str::<Decoded<Strkey>>(json) {
        Ok(_) => panic!("expected error for JSON with extra variant keys"),
        Err(e) => e,
    };
    assert!(
        err.to_string().contains("expected exactly one variant key"),
        "unexpected error message: {err}",
    );
}