Skip to content

Commit 390a560

Browse files
fix: only allow next authority to create billing extrinsics (#558)
1 parent 5ceed87 commit 390a560

File tree

8 files changed

+169
-57
lines changed

8 files changed

+169
-57
lines changed

substrate-node/Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

substrate-node/charts/substrate-node/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@ apiVersion: v2
22
name: substrate-node
33
description: Tfchain node
44
type: application
5-
version: 0.2.6
5+
version: 0.2.7
66
appVersion: '2.2.0-rc5'

substrate-node/charts/substrate-node/templates/deployment.yaml

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -155,25 +155,6 @@ spec:
155155
- name: keys
156156
mountPath: /keys
157157
readOnly: true
158-
- name: insert-smct-key
159-
securityContext:
160-
{{- toYaml .Values.securityContext | nindent 12 }}
161-
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
162-
imagePullPolicy: {{ .Values.image.pullPolicy }}
163-
args: [
164-
"key",
165-
"insert",
166-
"--keystore-path=/keystore",
167-
"--key-type", "smct",
168-
"--suri","/keys/smct",
169-
"--scheme=sr25519"
170-
]
171-
volumeMounts:
172-
- name: keystore
173-
mountPath: /keystore
174-
- name: keys
175-
mountPath: /keys
176-
readOnly: true
177158
{{ end }}
178159
volumes:
179160
- name: keystore

substrate-node/charts/substrate-node/values.yaml

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
image:
66
repository: tfchainnode
77
pullPolicy: IfNotPresent
8-
tag: ""
8+
tag: ''
99

1010
imagePullSecrets: []
11-
nameOverride: ""
12-
fullnameOverride: ""
11+
nameOverride: ''
12+
fullnameOverride: ''
1313

1414
serviceAccount:
1515
# Specifies whether a service account should be created
@@ -18,7 +18,7 @@ serviceAccount:
1818
annotations: {}
1919
# The name of the service account to use.
2020
# If not set and create is true, a name is generated using the fullname template
21-
name: ""
21+
name: ''
2222

2323
podAnnotations: {}
2424

@@ -39,15 +39,15 @@ service:
3939
type: ClusterIP
4040
port: 80
4141

42-
name: "tfchainnode01"
42+
name: 'tfchainnode01'
4343
port: 30333
4444
ws_port: 9944
4545
rpc_port: 9933
4646
is_validator: true
47-
chainspec: "/etc/chainspecs/dev/chainSpecRaw.json"
47+
chainspec: '/etc/chainspecs/dev/chainSpecRaw.json'
4848
ws_max_connections: 1048576
4949
#rpc_methods: "Unsafe"
50-
rpc_methods: "safe"
50+
rpc_methods: 'safe'
5151
disable_offchain_worker: true
5252
# telemetry_url: ""
5353

@@ -60,9 +60,6 @@ keys: []
6060
# secret: 1a...
6161
# - name: tft
6262
# secret: "kkjghjfkkj kjhgkkhhgg"
63-
## Smart contract billing offchain worker key
64-
# - name: smct
65-
# secret: "fsfdsfsdf"
6663

6764
# boot_node: "/ip4/10.42.1.134/tcp/30333/p2p/12D3KooWGX8JFxZu2dDmGVpa9t9enZnFCLhH4NUBA7PDuhEVQTMg"
6865

@@ -94,10 +91,10 @@ resources:
9491

9592
volume:
9693
size: 10Gi
97-
existingpersistentVolumeClaim: ""
94+
existingpersistentVolumeClaim: ''
9895
persistentVolume:
9996
create: true
100-
hostPath: "/chain01"
97+
hostPath: '/chain01'
10198

10299
nodeSelector: {}
103100

@@ -106,4 +103,4 @@ tolerations: []
106103
affinity: {}
107104

108105
threefoldVdc:
109-
backup: ""
106+
backup: ''

substrate-node/pallets/pallet-smart-contract/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0
4242
frame-try-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false, optional = true }
4343
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false }
4444
pallet-authorship = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false }
45+
pallet-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false }
46+
4547
parking_lot = '0.12.1'
4648

4749
# Benchmarking
@@ -50,6 +52,7 @@ frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch =
5052
[dev-dependencies]
5153
sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.24", default-features = false }
5254
env_logger = "*"
55+
substrate-validator-set = { path = "../substrate-validator-set" }
5356

5457
[features]
5558
default = ['std']

substrate-node/pallets/pallet-smart-contract/src/lib.rs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use frame_system::{
1919
self as system, ensure_signed,
2020
offchain::{AppCrypto, CreateSignedTransaction, SendSignedTransaction, Signer},
2121
};
22-
2322
pub use pallet::*;
2423
use pallet_authorship;
2524
use pallet_tfgrid;
@@ -28,7 +27,7 @@ use pallet_tfgrid::types as pallet_tfgrid_types;
2827
use pallet_timestamp as timestamp;
2928
use sp_core::crypto::KeyTypeId;
3029
use sp_runtime::{
31-
traits::{CheckedSub, SaturatedConversion},
30+
traits::{CheckedSub, Convert, SaturatedConversion},
3231
Perbill,
3332
};
3433
use substrate_fixed::types::U64F64;
@@ -38,7 +37,7 @@ use tfchain_support::{
3837
types::PublicIP,
3938
};
4039

41-
pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"smct");
40+
pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"aura");
4241

4342
#[cfg(test)]
4443
mod mock;
@@ -211,6 +210,7 @@ pub mod pallet {
211210
+ pallet_tfgrid::Config
212211
+ pallet_tft_price::Config
213212
+ pallet_authorship::Config
213+
+ pallet_session::Config
214214
{
215215
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
216216
type Currency: LockableCurrency<Self::AccountId>;
@@ -341,7 +341,6 @@ pub mod pallet {
341341
NodeNotAvailableToDeploy,
342342
CannotUpdateContractInGraceState,
343343
NumOverflow,
344-
OffchainSignedTxNotBlockAuthor,
345344
OffchainSignedTxCannotSign,
346345
OffchainSignedTxAlreadySent,
347346
OffchainSignedTxNoLocalAccountAvailable,
@@ -351,6 +350,8 @@ pub mod pallet {
351350
NoSuchSolutionProvider,
352351
SolutionProviderNotApproved,
353352
CanOnlyIncreaseFrequency,
353+
IsNotAnAuthority,
354+
WrongAuthority,
354355
}
355356

356357
#[pallet::genesis_config]
@@ -941,8 +942,8 @@ impl<T: Config> Pallet<T> {
941942
fn bill_contract_using_signed_transaction(contract_id: u64) -> Result<(), Error<T>> {
942943
let signer = Signer::<T, <T as pallet::Config>::AuthorityId>::any_account();
943944

944-
// Only allow the author of the block to trigger the billing
945-
Self::is_block_author(&signer)?;
945+
// Only allow the author of the next block to trigger the billing
946+
Self::is_next_block_author(&signer)?;
946947

947948
if !signer.can_sign() {
948949
log::error!(
@@ -1795,14 +1796,36 @@ impl<T: Config> PublicIpModifier for Pallet<T> {
17951796
}
17961797

17971798
impl<T: Config> Pallet<T> {
1798-
fn is_block_author(signer: &Signer<T, <T as Config>::AuthorityId>) -> Result<(), Error<T>> {
1799+
// Validates if the given signer is the next block author based on the validators in session
1800+
// This can be used if an extrinsic should be refunded by the author in the same block
1801+
// It also requires that the keytype inserted for the offchain workers is the validator key
1802+
fn is_next_block_author(
1803+
signer: &Signer<T, <T as Config>::AuthorityId>,
1804+
) -> Result<(), Error<T>> {
17991805
let author = <pallet_authorship::Pallet<T>>::author();
1806+
let validators = <pallet_session::Pallet<T>>::validators();
18001807

1808+
// Sign some arbitrary data in order to get the AccountId, maybe there is another way to do this?
18011809
let signed_message = signer.sign_message(&[0]);
18021810
if let Some(signed_message_data) = signed_message {
18031811
if let Some(block_author) = author {
1804-
if signed_message_data.0.id != block_author {
1805-
return Err(Error::<T>::OffchainSignedTxNotBlockAuthor);
1812+
let validator =
1813+
<T as pallet_session::Config>::ValidatorIdOf::convert(block_author.clone())
1814+
.ok_or(Error::<T>::IsNotAnAuthority)?;
1815+
1816+
let validator_count = validators.len();
1817+
let author_index = (validators.iter().position(|a| a == &validator).unwrap_or(0)
1818+
+ 1)
1819+
% validator_count;
1820+
1821+
let signer_validator_account =
1822+
<T as pallet_session::Config>::ValidatorIdOf::convert(
1823+
signed_message_data.0.id.clone(),
1824+
)
1825+
.ok_or(Error::<T>::IsNotAnAuthority)?;
1826+
1827+
if signer_validator_account != validators[author_index] {
1828+
return Err(Error::<T>::WrongAuthority);
18061829
}
18071830
}
18081831
}

substrate-node/pallets/pallet-smart-contract/src/mock.rs

Lines changed: 117 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ use pallet_tfgrid::{
2121
twin::TwinIp,
2222
DocumentHashInput, DocumentLinkInput, TwinIpInput,
2323
};
24-
use pallet_tfgrid::{CityNameInput, CountryNameInput, LatitudeInput, LongitudeInput, Ip4Input, Gw4Input};
24+
use pallet_tfgrid::{
25+
CityNameInput, CountryNameInput, Gw4Input, Ip4Input, LatitudeInput, LongitudeInput,
26+
};
2527
use parking_lot::RwLock;
2628
use sp_core::{
29+
crypto::key_types::DUMMY,
2730
crypto::Ss58Codec,
2831
offchain::{
2932
testing::{self},
@@ -32,16 +35,55 @@ use sp_core::{
3235
sr25519, Pair, Public, H256,
3336
};
3437
use sp_keystore::{testing::KeyStore, KeystoreExt, SyncCryptoStore};
35-
use sp_runtime::traits::{IdentifyAccount, Verify};
36-
use sp_runtime::{offchain::TransactionPool, MultiSignature};
3738
use sp_runtime::{
38-
testing::{Header, TestXt},
39-
traits::{BlakeTwo256, Extrinsic as ExtrinsicT, IdentityLookup},
40-
AccountId32,
39+
impl_opaque_keys,
40+
offchain::TransactionPool,
41+
testing::{Header, TestXt, UintAuthorityId},
42+
traits::{
43+
BlakeTwo256, Extrinsic as ExtrinsicT, IdentifyAccount, IdentityLookup, OpaqueKeys, Verify,
44+
},
45+
AccountId32, MultiSignature,
4146
};
4247
use sp_std::convert::{TryFrom, TryInto};
43-
use tfchain_support::traits::ChangeNode;
48+
use std::cell::RefCell;
49+
use tfchain_support::{constants::time::MINUTES, traits::ChangeNode};
50+
51+
impl_opaque_keys! {
52+
pub struct MockSessionKeys {
53+
pub dummy: UintAuthorityId,
54+
}
55+
}
56+
57+
impl From<UintAuthorityId> for MockSessionKeys {
58+
fn from(dummy: UintAuthorityId) -> Self {
59+
Self { dummy }
60+
}
61+
}
4462

63+
pub const KEY_ID_A: KeyTypeId = KeyTypeId([4; 4]);
64+
pub const KEY_ID_B: KeyTypeId = KeyTypeId([9; 4]);
65+
66+
#[derive(Debug, Clone, codec::Encode, codec::Decode, PartialEq, Eq)]
67+
pub struct PreUpgradeMockSessionKeys {
68+
pub a: [u8; 32],
69+
pub b: [u8; 64],
70+
}
71+
72+
impl OpaqueKeys for PreUpgradeMockSessionKeys {
73+
type KeyTypeIdProviders = ();
74+
75+
fn key_ids() -> &'static [KeyTypeId] {
76+
&[KEY_ID_A, KEY_ID_B]
77+
}
78+
79+
fn get_raw(&self, i: KeyTypeId) -> &[u8] {
80+
match i {
81+
i if i == KEY_ID_A => &self.a[..],
82+
i if i == KEY_ID_B => &self.b[..],
83+
_ => &[],
84+
}
85+
}
86+
}
4587
// set environment variable RUST_LOG=debug to see all logs when running the tests and call
4688
// env_logger::init() at the beginning of the test
4789
use env_logger;
@@ -68,6 +110,8 @@ construct_runtime!(
68110
SmartContractModule: pallet_smart_contract::{Pallet, Call, Storage, Event<T>},
69111
TFTPriceModule: pallet_tft_price::{Pallet, Call, Storage, Event<T>},
70112
Authorship: pallet_authorship::{Pallet, Call, Storage, Inherent},
113+
ValidatorSet: substrate_validator_set::{Pallet, Call, Storage, Event<T>, Config<T>},
114+
Session: pallet_session::{Pallet, Call, Storage, Event, Config<T>},
71115
}
72116
);
73117

@@ -246,6 +290,72 @@ impl pallet_authorship::Config for TestRuntime {
246290
type EventHandler = ();
247291
}
248292

293+
parameter_types! {
294+
pub const Period: u32 = 60 * MINUTES;
295+
pub const Offset: u32 = 0;
296+
}
297+
298+
thread_local! {
299+
pub static VALIDATORS: RefCell<Vec<u64>> = RefCell::new(vec![1, 2, 3]);
300+
pub static NEXT_VALIDATORS: RefCell<Vec<u64>> = RefCell::new(vec![1, 2, 3]);
301+
pub static AUTHORITIES: RefCell<Vec<UintAuthorityId>> =
302+
RefCell::new(vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]);
303+
pub static FORCE_SESSION_END: RefCell<bool> = RefCell::new(false);
304+
pub static SESSION_LENGTH: RefCell<u64> = RefCell::new(2);
305+
pub static SESSION_CHANGED: RefCell<bool> = RefCell::new(false);
306+
pub static DISABLED: RefCell<bool> = RefCell::new(false);
307+
pub static BEFORE_SESSION_END_CALLED: RefCell<bool> = RefCell::new(false);
308+
}
309+
310+
use pallet_session::SessionHandler;
311+
use sp_runtime::RuntimeAppPublic;
312+
pub struct TestSessionHandler;
313+
impl SessionHandler<AccountId> for TestSessionHandler {
314+
const KEY_TYPE_IDS: &'static [sp_runtime::KeyTypeId] = &[UintAuthorityId::ID];
315+
fn on_genesis_session<T: OpaqueKeys>(_validators: &[(AccountId, T)]) {}
316+
fn on_new_session<T: OpaqueKeys>(
317+
changed: bool,
318+
validators: &[(AccountId, T)],
319+
_queued_validators: &[(AccountId, T)],
320+
) {
321+
SESSION_CHANGED.with(|l| *l.borrow_mut() = changed);
322+
AUTHORITIES.with(|l| {
323+
*l.borrow_mut() = validators
324+
.iter()
325+
.map(|(_, id)| id.get::<UintAuthorityId>(DUMMY).unwrap_or_default())
326+
.collect()
327+
});
328+
}
329+
fn on_disabled(_validator_index: u32) {
330+
DISABLED.with(|l| *l.borrow_mut() = true)
331+
}
332+
fn on_before_session_ending() {
333+
BEFORE_SESSION_END_CALLED.with(|b| *b.borrow_mut() = true);
334+
}
335+
}
336+
337+
parameter_types! {
338+
pub const MinAuthorities: u32 = 2;
339+
}
340+
341+
impl substrate_validator_set::Config for TestRuntime {
342+
type AddRemoveOrigin = EnsureRoot<Self::AccountId>;
343+
type Event = Event;
344+
type MinAuthorities = MinAuthorities;
345+
}
346+
347+
impl pallet_session::Config for TestRuntime {
348+
type Event = Event;
349+
type ValidatorId = <Self as frame_system::Config>::AccountId;
350+
type ValidatorIdOf = substrate_validator_set::ValidatorOf<Self>;
351+
type ShouldEndSession = pallet_session::PeriodicSessions<Period, Offset>;
352+
type NextSessionRotation = pallet_session::PeriodicSessions<Period, Offset>;
353+
type SessionManager = ();
354+
type SessionHandler = TestSessionHandler;
355+
type Keys = MockSessionKeys;
356+
type WeightInfo = ();
357+
}
358+
249359
type AccountPublic = <MultiSignature as Verify>::Signer;
250360

251361
pub(crate) fn get_name_contract_name(contract_name_input: &[u8]) -> TestNameContractName {

0 commit comments

Comments
 (0)