1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
use crate::constants::VM_DEFAULT_FRAGMENT_NAME;
use crate::state::{DidAccount, VerificationMethodFlags, VerificationMethodType};
use crate::{Service, VerificationMethod};
use anchor_lang::prelude::*;
use anchor_lang::{AccountDeserialize, AccountSerialize, Owner};
use borsh::BorshDeserialize;
use num_traits::*;
use std::str::FromStr;

#[derive(Clone, BorshDeserialize)]
pub struct LegacyServiceEndpoint {
    /// Id related to the endpoint
    /// When the DID document is resolved, this is concatenated to the DID to produce
    /// did:mln:<identifier>#<id>
    pub id: String,
    /// Endpoint type
    pub endpoint_type: String,
    /// The actual URL of the endpoint
    pub endpoint: String,
    /// More info about the endpoint
    pub description: String,
}

impl LegacyServiceEndpoint {
    pub fn post_migration_size(&self) -> usize {
        4 + self.id.len() + 4 + self.endpoint_type.len() + 4 + self.endpoint.len()
    }
}

#[derive(Clone, BorshDeserialize)]
pub struct LegacyVerificationMethod {
    /// Unique id for the verification method, and how to find it
    /// When the DID document is resolved, this is concatenated to the DID to produce
    /// did:mln:<identifier>#<id>
    pub id: String,
    /// What kind of key this is. TODO use an enum?
    pub verification_type: String,
    /// The associated pubkey itself
    pub pubkey: Pubkey,
}

impl LegacyVerificationMethod {
    pub fn post_migration_size(&self) -> usize {
        4 + self.id.len() // fragment string
            + 2 // flags
            + 1 // method
            + 4 + 32 // ed25519 pubkey
    }
}

#[derive(Clone, BorshDeserialize)]
pub struct LegacyDidAccount {
    /// The version of the account for (de)serialization
    pub account_version: u8,
    /// The public key of the solData account - used to derive the identifier
    /// and first verification method
    pub authority: Pubkey,

    /// DecentralizedIdentifier version - used to generate the DID JSON-LD context:
    /// ["https://w3id.org/did/v1.0", "https://w3id.org/mln/v" +  version]
    pub version: String,
    /// List of controllers of this did, using the key of the did
    pub controller: Vec<Pubkey>,

    /// All of the public keys related to the DecentralizedIdentifier
    pub verification_method: Vec<LegacyVerificationMethod>,
    pub authentication: Vec<String>,
    /// Currently the most important part, decides which ID gets to do things
    pub capability_invocation: Vec<String>,
    pub capability_delegation: Vec<String>,
    pub key_agreement: Vec<String>,
    pub assertion_method: Vec<String>,
    /// Services that can be used with this DID
    pub service: Vec<LegacyServiceEndpoint>,
}

impl LegacyDidAccount {
    pub fn post_migration_size(&self) -> usize {
        let mut size = DidAccount::initial_size();
        size += self
            .verification_method
            .iter()
            .fold(0, |accum, item| accum + item.post_migration_size()); // verification_methods
        size += self
            .service
            .iter()
            .fold(0, |accum, item| accum + item.post_migration_size()); // services
        size += self.controller.len() * 32; // native_controllers
        size += 8; // anchor discriminator
        size
    }

    pub fn migrate(&self, into: &mut DidAccount, bump: u8) -> Result<()> {
        // "default" authority always has proven ownership and is protected of the DID
        let default_flags = self.get_flags(&VM_DEFAULT_FRAGMENT_NAME.to_string())
            | VerificationMethodFlags::OWNERSHIP_PROOF
            | VerificationMethodFlags::PROTECTED;
        into.init(bump, &self.authority, default_flags);
        let migrated = self.migrate_verification_methods();
        into.set_verification_methods(Vec::new(), migrated)?;
        let migrated = self.migrate_services();
        into.set_services(migrated, false)?;
        into.set_native_controllers(self.controller.clone())?;

        // TODO: Documentation
        // pub version: String,
        // pub account_version: u8, unhandled

        Ok(())
    }

    fn migrate_verification_methods(&self) -> Vec<VerificationMethod> {
        // Note, this migration with fail on duplicates ids OR another "default"
        // in self.verification_method
        self.verification_method
            .iter()
            .map(|vm| VerificationMethod {
                fragment: vm.id.clone(),
                method_type: VerificationMethodType::Ed25519VerificationKey2018
                    .to_u8()
                    .unwrap(),
                flags: self.get_flags(&vm.id).bits(),
                key_data: vm.pubkey.to_bytes().to_vec(),
            })
            .collect()
    }

    fn get_flags(&self, vm_fragment: &String) -> VerificationMethodFlags {
        let mut flags = VerificationMethodFlags::NONE;

        if self.authentication.contains(vm_fragment) {
            flags |= VerificationMethodFlags::AUTHENTICATION;
        }
        if self.assertion_method.contains(vm_fragment) {
            flags |= VerificationMethodFlags::ASSERTION;
        }
        if self.capability_invocation.contains(vm_fragment) {
            flags |= VerificationMethodFlags::CAPABILITY_INVOCATION;
        }
        if self.capability_delegation.contains(vm_fragment) {
            flags |= VerificationMethodFlags::CAPABILITY_DELEGATION;
        }
        if self.key_agreement.contains(vm_fragment) {
            flags |= VerificationMethodFlags::KEY_AGREEMENT;
        }

        // special case for inferred "capability_invocation" verification method
        if self.capability_invocation.is_empty() && vm_fragment == VM_DEFAULT_FRAGMENT_NAME {
            flags |= VerificationMethodFlags::CAPABILITY_INVOCATION;
        }

        flags
    }

    fn migrate_services(&self) -> Vec<Service> {
        self.service
            .iter()
            .map(|se| {
                Service {
                    fragment: se.id.clone(),
                    service_type: se.endpoint_type.clone(),
                    service_endpoint: se.endpoint.clone(),
                    // TODO: Documentation NOTE, Descriptions will be dropped
                    // service_description: se.description.clone(),
                }
            })
            .collect()
    }
}

impl AccountDeserialize for LegacyDidAccount {
    fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self> {
        // Deserialize whole account (without discriminator)
        AnchorDeserialize::deserialize(buf).map_err(|_| ErrorCode::AccountDidNotDeserialize.into())
    }
}

impl AccountSerialize for LegacyDidAccount {}

impl Owner for LegacyDidAccount {
    fn owner() -> Pubkey {
        // TODO: Make static
        Pubkey::from_str("idDa4XeCjVwKcprVAo812coUQbovSZ4kDGJf2sPaBnM").unwrap()
    }
}