1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
//! # Chain Snapshot Primitives
18
//!
19
//! This crate defines those primitives to retrieve keys from a defined Backend
20

            
21
#![cfg_attr(not(feature = "std"), no_std)]
22

            
23
use {
24
    parity_scale_codec::Decode,
25
    sp_runtime::traits::HashingFor,
26
    sp_state_machine::{Backend, TrieBackend, TrieBackendBuilder},
27
    sp_trie::{HashDBT, MemoryDB, StorageProof, EMPTY_PREFIX},
28
};
29

            
30
#[derive(Debug)]
31
pub enum ReadEntryErr {
32
    /// The value cannot be extracted from the proof.
33
    Proof,
34
    /// The value cannot be decoded.
35
    Decode,
36
    /// The value is expected to be present on the relay chain, but it doesn't exist.
37
    Absent,
38
    /// The proof provided does not match the root provided
39
    RootMismatch,
40
}
41

            
42
/// Read an entry given by the key and try to decode it. If the value specified by the key according
43
/// to the proof is empty, the `fallback` value will be returned.
44
///
45
/// Returns `Err` in case the backend can't return the value under the specific key (likely due to
46
/// a malformed proof), in case the decoding fails, or in case where the value is empty in the relay
47
/// chain state and no fallback was provided.
48
fn read_entry<T, B, Block>(backend: &B, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr>
49
where
50
    T: Decode,
51
    B: Backend<HashingFor<Block>>,
52
    Block: sp_runtime::traits::Block,
53
{
54
    backend
55
        .storage(key)
56
        .map_err(|_| ReadEntryErr::Proof)?
57
        .map(|raw_entry| T::decode(&mut &raw_entry[..]).map_err(|_| ReadEntryErr::Decode))
58
        .transpose()?
59
        .or(fallback)
60
        .ok_or(ReadEntryErr::Absent)
61
}
62

            
63
/// Read an optional entry given by the key and try to decode it.
64
/// Returns `None` if the value specified by the key according to the proof is empty.
65
///
66
/// Returns `Err` in case the backend can't return the value under the specific key (likely due to
67
/// a malformed proof) or if the value couldn't be decoded.
68
fn read_optional_entry<T, B, Block>(backend: &B, key: &[u8]) -> Result<Option<T>, ReadEntryErr>
69
where
70
    T: Decode,
71
    B: Backend<HashingFor<Block>>,
72
    Block: sp_runtime::traits::Block,
73
{
74
    match read_entry::<T, B, Block>(backend, key, None) {
75
        Ok(v) => Ok(Some(v)),
76
        Err(ReadEntryErr::Absent) => Ok(None),
77
        Err(err) => Err(err),
78
    }
79
}
80

            
81
/// A state proof extracted from the relay chain.
82
///
83
/// This state proof is extracted from the relay chain block we are building on top of.
84
pub struct GenericStateProof<Block: sp_runtime::traits::Block> {
85
    trie_backend: TrieBackend<MemoryDB<HashingFor<Block>>, HashingFor<Block>>,
86
}
87

            
88
impl<Block: sp_runtime::traits::Block> GenericStateProof<Block> {
89
    /// Create a new instance of `Self`.
90
    ///
91
    /// Returns an error if the given `relay_parent_storage_root` is not the root of the given
92
    /// `proof`.
93
    pub fn new(
94
        relay_parent_storage_root: Block::Hash,
95
        proof: StorageProof,
96
    ) -> Result<Self, ReadEntryErr> {
97
        // Retrieve whether the proof is empty
98
        let proof_empty = proof.is_empty();
99

            
100
        let db = proof.into_memory_db::<HashingFor<Block>>();
101
        // If the proof is empty we should not compare against any root, but rather, expect that the pallet
102
        // will dot he job when looking for certain keys
103
        if !db.contains(&relay_parent_storage_root, EMPTY_PREFIX) && !proof_empty {
104
            return Err(ReadEntryErr::RootMismatch);
105
        }
106
        let trie_backend = TrieBackendBuilder::new(db, relay_parent_storage_root).build();
107

            
108
        Ok(Self { trie_backend })
109
    }
110

            
111
    /// Read an entry given by the key and try to decode it. If the value specified by the key according
112
    /// to the proof is empty, the `fallback` value will be returned.
113
    ///
114
    /// Returns `Err` in case the backend can't return the value under the specific key (likely due to
115
    /// a malformed proof), in case the decoding fails, or in case where the value is empty in the relay
116
    /// chain state and no fallback was provided.
117
    pub fn read_entry<T>(&self, key: &[u8], fallback: Option<T>) -> Result<T, ReadEntryErr>
118
    where
119
        T: Decode,
120
    {
121
        read_entry::<T, TrieBackend<MemoryDB<HashingFor<Block>>, HashingFor<Block>>, Block>(
122
            &self.trie_backend,
123
            key,
124
            fallback,
125
        )
126
    }
127

            
128
    /// Read an optional entry given by the key and try to decode it.
129
    ///
130
    /// Returns `Err` in case the backend can't return the value under the specific key (likely due to
131
    /// a malformed proof) or if the value couldn't be decoded.
132
    pub fn read_optional_entry<T>(&self, key: &[u8]) -> Result<Option<T>, ReadEntryErr>
133
    where
134
        T: Decode,
135
    {
136
        read_optional_entry::<T, TrieBackend<MemoryDB<HashingFor<Block>>, HashingFor<Block>>, Block>(
137
            &self.trie_backend,
138
            key,
139
        )
140
    }
141
}