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
use {
18
    container_chain_template_frontier_runtime::{
19
        AccountId, EVMChainIdConfig, EVMConfig, MaintenanceModeConfig, MigrationsConfig,
20
        PolkadotXcmConfig, Precompiles,
21
    },
22
    cumulus_primitives_core::ParaId,
23
    fp_evm::GenesisAccount,
24
    hex_literal::hex,
25
    sc_chain_spec::{ChainSpecExtension, ChainSpecGroup},
26
    sc_network::config::MultiaddrWithPeerId,
27
    sc_service::ChainType,
28
    serde::{Deserialize, Serialize},
29
};
30

            
31
/// Specialized `ChainSpec` for the normal parachain runtime.
32
pub type ChainSpec = sc_service::GenericChainSpec<Extensions>;
33

            
34
/// Orcherstrator's parachain id
35
pub const ORCHESTRATOR: ParaId = ParaId::new(1000);
36

            
37
/// The extensions for the [`ChainSpec`].
38
694
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)]
39
#[serde(deny_unknown_fields)]
40
pub struct Extensions {
41
    /// The relay chain of the Parachain.
42
    pub relay_chain: String,
43
    /// The id of the Parachain.
44
    pub para_id: u32,
45
}
46

            
47
impl Extensions {
48
    /// Try to get the extension from the given `ChainSpec`.
49
414
    pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> {
50
414
        sc_chain_spec::get_extension(chain_spec.extensions())
51
414
    }
52
}
53

            
54
2
pub fn development_config(para_id: ParaId, boot_nodes: Vec<String>) -> ChainSpec {
55
2
    // Give your base currency a unit name and decimal places
56
2
    let mut properties = sc_chain_spec::Properties::new();
57
2
    properties.insert("tokenSymbol".into(), "UNIT".into());
58
2
    properties.insert("tokenDecimals".into(), 18.into());
59
2
    properties.insert("ss58Format".into(), 42.into());
60
2
    properties.insert("isEthereum".into(), true.into());
61
2

            
62
2
    let mut default_funded_accounts = pre_funded_accounts();
63
2
    default_funded_accounts.sort();
64
2
    default_funded_accounts.dedup();
65
2
    let boot_nodes: Vec<MultiaddrWithPeerId> = boot_nodes
66
2
        .into_iter()
67
2
        .map(|x| {
68
            x.parse::<MultiaddrWithPeerId>()
69
                .unwrap_or_else(|e| panic!("invalid bootnode address format {:?}: {:?}", x, e))
70
2
        })
71
2
        .collect();
72
2

            
73
2
    ChainSpec::builder(
74
2
        container_chain_template_frontier_runtime::WASM_BINARY
75
2
            .expect("WASM binary was not built, please build it!"),
76
2
        Extensions {
77
2
            relay_chain: "rococo-local".into(), // You MUST set this to the correct network!
78
2
            para_id: para_id.into(),
79
2
        },
80
2
    )
81
2
    .with_name("Development")
82
2
    .with_id("dev")
83
2
    .with_chain_type(ChainType::Development)
84
2
    .with_genesis_config(testnet_genesis(
85
2
        default_funded_accounts.clone(),
86
2
        para_id,
87
2
        AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")), // Alith
88
2
    ))
89
2
    .with_properties(properties)
90
2
    .with_boot_nodes(boot_nodes)
91
2
    .build()
92
2
}
93

            
94
pub fn local_testnet_config(para_id: ParaId, boot_nodes: Vec<String>) -> ChainSpec {
95
    // Give your base currency a unit name and decimal places
96
    let mut properties = sc_chain_spec::Properties::new();
97
    properties.insert("tokenSymbol".into(), "UNIT".into());
98
    properties.insert("tokenDecimals".into(), 18.into());
99
    properties.insert("ss58Format".into(), 42.into());
100
    properties.insert("isEthereum".into(), true.into());
101
    let protocol_id = format!("container-chain-{}", para_id);
102

            
103
    let mut default_funded_accounts = pre_funded_accounts();
104
    default_funded_accounts.sort();
105
    default_funded_accounts.dedup();
106
    let boot_nodes: Vec<MultiaddrWithPeerId> = boot_nodes
107
        .into_iter()
108
        .map(|x| {
109
            x.parse::<MultiaddrWithPeerId>()
110
                .unwrap_or_else(|e| panic!("invalid bootnode address format {:?}: {:?}", x, e))
111
        })
112
        .collect();
113

            
114
    ChainSpec::builder(
115
        container_chain_template_frontier_runtime::WASM_BINARY
116
            .expect("WASM binary was not built, please build it!"),
117
        Extensions {
118
            relay_chain: "rococo-local".into(), // You MUST set this to the correct network!
119
            para_id: para_id.into(),
120
        },
121
    )
122
    .with_name(&format!("Frontier Container {}", para_id))
123
    .with_id(&format!("frontier_container_{}", para_id))
124
    .with_chain_type(ChainType::Local)
125
    .with_genesis_config(testnet_genesis(
126
        default_funded_accounts.clone(),
127
        para_id,
128
        AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")), // Alith
129
    ))
130
    .with_properties(properties)
131
    .with_protocol_id(&protocol_id)
132
    .with_boot_nodes(boot_nodes)
133
    .build()
134
}
135

            
136
2
fn testnet_genesis(
137
2
    endowed_accounts: Vec<AccountId>,
138
2
    id: ParaId,
139
2
    root_key: AccountId,
140
2
) -> serde_json::Value {
141
2
    // This is the simplest bytecode to revert without returning any data.
142
2
    // We will pre-deploy it under all of our precompiles to ensure they can be called from
143
2
    // within contracts.
144
2
    // (PUSH1 0x00 PUSH1 0x00 REVERT)
145
2
    let revert_bytecode = vec![0x60, 0x00, 0x60, 0x00, 0xFD];
146
2

            
147
2
    let g = container_chain_template_frontier_runtime::RuntimeGenesisConfig {
148
2
        system: Default::default(),
149
2
        balances: container_chain_template_frontier_runtime::BalancesConfig {
150
2
            balances: endowed_accounts
151
2
                .iter()
152
2
                .cloned()
153
8
                .map(|k| (k, 1 << 80))
154
2
                .collect(),
155
2
        },
156
2
        parachain_info: container_chain_template_frontier_runtime::ParachainInfoConfig {
157
2
            parachain_id: id,
158
2
            ..Default::default()
159
2
        },
160
2
        parachain_system: Default::default(),
161
2
        // EVM compatibility
162
2
        // We should change this to something different than Moonbeam
163
2
        // For now moonwall is very tailored for moonbeam so we need it for tests
164
2
        evm_chain_id: EVMChainIdConfig {
165
2
            chain_id: 1281,
166
2
            ..Default::default()
167
2
        },
168
2
        evm: EVMConfig {
169
2
            // We need _some_ code inserted at the precompile address so that
170
2
            // the evm will actually call the address.
171
2
            accounts: Precompiles::used_addresses()
172
26
                .map(|addr| {
173
26
                    (
174
26
                        addr.into(),
175
26
                        GenesisAccount {
176
26
                            nonce: Default::default(),
177
26
                            balance: Default::default(),
178
26
                            storage: Default::default(),
179
26
                            code: revert_bytecode.clone(),
180
26
                        },
181
26
                    )
182
26
                })
183
2
                .collect(),
184
2
            ..Default::default()
185
2
        },
186
2
        ethereum: Default::default(),
187
2
        base_fee: Default::default(),
188
2
        transaction_payment: Default::default(),
189
2
        sudo: container_chain_template_frontier_runtime::SudoConfig {
190
2
            key: Some(root_key),
191
2
        },
192
2
        authorities_noting: container_chain_template_frontier_runtime::AuthoritiesNotingConfig {
193
2
            orchestrator_para_id: ORCHESTRATOR,
194
2
            ..Default::default()
195
2
        },
196
2
        migrations: MigrationsConfig {
197
2
            ..Default::default()
198
2
        },
199
2
        maintenance_mode: MaintenanceModeConfig {
200
2
            start_in_maintenance_mode: false,
201
2
            ..Default::default()
202
2
        },
203
2
        // This should initialize it to whatever we have set in the pallet
204
2
        polkadot_xcm: PolkadotXcmConfig::default(),
205
2
        tx_pause: Default::default(),
206
2
    };
207
2

            
208
2
    serde_json::to_value(g).unwrap()
209
2
}
210

            
211
/// Get pre-funded accounts
212
2
pub fn pre_funded_accounts() -> Vec<AccountId> {
213
2
    // These addresses are derived from Substrate's canonical mnemonic:
214
2
    // bottom drive obey lake curtain smoke basket hold race lonely fit walk
215
2
    vec![
216
2
        AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")), // Alith
217
2
        AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")), // Baltathar
218
2
        AccountId::from(hex!("798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc")), // Charleth
219
2
        AccountId::from(hex!("773539d4Ac0e786233D90A233654ccEE26a613D9")), // Dorothy
220
2
    ]
221
2
}