Skip to content

Commit

Permalink
Basic support for System RPC API (#308)
Browse files Browse the repository at this point in the history
* Add basic support for SystemApi

* Fix documentation and melos analyze script

* Support all system RPC methods

* dart format

* Add unit tests

* dart analyze

* dart analyze
  • Loading branch information
Lohann authored Jul 8, 2023
1 parent e0b4c0d commit 3b8a154
Show file tree
Hide file tree
Showing 20 changed files with 947 additions and 30 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:

- name: Export FVM settings as environment variables
uses: kuhnroyal/flutter-fvm-config-action@v1

- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ services:
tests:
build:
context: .
dockerfile: docker/Dockerfile
dockerfile: docker/Dockerfile
6 changes: 3 additions & 3 deletions melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ scripts:
run: fvm dart run melos exec --scope="${SCOPE:-*}" -c 1 --fail-fast -- 'fvm dart test --coverage="coverage"'

format:
run: fvm dart run melos exec --scope="${SCOPE:-*}" -c 1 --fail-fast -- 'fvm dart format --set-exit-if-changed .'
run: fvm dart run melos exec --scope="${SCOPE:-*}" -c 1 --fail-fast -- 'fvm dart format .'

analyze:
run: fvm dart run melos exec --scope="${SCOPE:-*}" -c 1 --fail-fast -- 'fvm dart analyze .'
run: fvm dart run melos exec --scope="${SCOPE:-*}" -c 1 --fail-fast -- 'fvm dart analyze --fatal-infos .'

# CI commands
# OBS: CI we do not use FVM
Expand All @@ -29,4 +29,4 @@ scripts:
run: dart run melos exec --scope="${SCOPE:-*}" -c 1 --fail-fast -- 'dart format --set-exit-if-changed .'

analyze:ci:
run: dart run melos exec --scope="${SCOPE:-*}" -c 1 --fail-fast -- 'dart analyze .'
run: dart run melos exec --scope="${SCOPE:-*}" -c 1 --fail-fast -- 'dart analyze --fatal-infos .'
2 changes: 1 addition & 1 deletion packages/polkadart/example/polkadart_example.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:polkadart/polkadart.dart' show Provider, StateApi;

void main() async {
final polkadart = Provider(Uri.parse('wss://kusama-rpc.polkadot.io'));
final polkadart = Provider.fromUri(Uri.parse('wss://kusama-rpc.polkadot.io'));
final api = StateApi(polkadart);
final runtimeVersion = await api.getRuntimeVersion();
print(runtimeVersion.toJson());
Expand Down
9 changes: 7 additions & 2 deletions packages/polkadart/lib/apis/apis.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ import 'dart:async' show Future, StreamSubscription;
import 'package:convert/convert.dart' show hex;
import '../primitives/primitives.dart'
show
RuntimeVersion,
RuntimeMetadata,
BlockHash,
ChainType,
Health,
KeyValue,
RuntimeVersion,
RuntimeMetadata,
PeerInfo,
SyncState,
StorageKey,
StorageData,
StorageChangeSet,
ReadProof;
import '../../provider.dart' show Provider;

part './state.dart';
part './system.dart';
12 changes: 6 additions & 6 deletions packages/polkadart/lib/apis/state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ class StateApi<P extends Provider> {
if (at != null) {
params.add('0x${hex.encode(at)}');
}
final result = await _provider.send('state_call', params);
final data = result.result as String;
final response = await _provider.send('state_call', params);
final data = response.result as String;
return Uint8List.fromList(hex.decode(data.substring(2)));
}

Expand Down Expand Up @@ -63,8 +63,8 @@ class StateApi<P extends Provider> {
if (at != null) {
params.add('0x${hex.encode(at)}');
}
final result = await _provider.send('state_getStorage', params);
final data = result.result as String?;
final response = await _provider.send('state_getStorage', params);
final data = response.result as String?;
return data == null
? null
: Uint8List.fromList(hex.decode(data.substring(2)));
Expand All @@ -76,8 +76,8 @@ class StateApi<P extends Provider> {
if (at != null) {
params.add('0x${hex.encode(at)}');
}
final result = await _provider.send('state_getStorageHash', params);
final data = result.result as String?;
final response = await _provider.send('state_getStorageHash', params);
final data = response.result as String?;
return data == null
? null
: Uint8List.fromList(hex.decode(data.substring(2)));
Expand Down
153 changes: 153 additions & 0 deletions packages/polkadart/lib/apis/system.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
part of apis;

/// Substrate system RPC API
class SystemApi<P extends Provider, H, N> {
final P _provider;

const SystemApi(this._provider);

/// Get the node's implementation name. Plain old string.
Future<String> name() async {
final response = await _provider.send('system_name', []);
return response.result as String;
}

/// Get the node implementation's version. Should be a semver string.
Future<String> version() async {
final response = await _provider.send('system_version', []);
return response.result as String;
}

/// Get the chain's name. Given as a string identifier.
Future<String> chain() async {
final response = await _provider.send('system_chain', []);
return response.result as String;
}

/// Get the chain's type.
Future<ChainType> chainType() async {
final response = await _provider.send('system_chainType', []);
return ChainType.fromJson(response.result);
}

/// Return health status of the node.
///
/// Node is considered healthy if it is:
/// - connected to some peers (unless running in dev mode)
/// - not performing a major sync
Future<Health> health() async {
final response = await _provider.send('system_health', []);
return Health.fromJson(response.result as Map<String, dynamic>);
}

/// Returns the base58-encoded PeerId of the node.
Future<String> localPeerId() async {
final response = await _provider.send('system_localPeerId', []);
return response.result as String;
}

/// Returns the multi-addresses that the local node is listening on
///
/// The addresses include a trailing `/p2p/` with the local PeerId, and are thus suitable to
/// be passed to `addReservedPeer` or as a bootnode address for example.
Future<List<String>> localListenAddresses() async {
final response = await _provider.send('system_localListenAddresses', []);
return (response.result as List).cast<String>().toList();
}

/// Returns currently connected peers
///
/// unsafe: This method is only active with appropriate flags
Future<List<PeerInfo<H, N>>> peers() async {
final response = await _provider.send('system_peers', []);
return (response.result as List)
.cast<Map<String, dynamic>>()
.map((json) => PeerInfo<H, N>.fromJson(json))
.toList();
}

/// Returns the next valid index (aka nonce) for given account.
///
/// This method takes into consideration all pending transactions
/// currently in the pool and if no transactions are found in the pool
/// it fallbacks to query the index from the runtime (aka. state nonce).
Future<int> accountNextIndex(String account) async {
final response = await _provider.send('system_accountNextIndex', [account]);
return response.result as int;
}

/// Dry run an extrinsic at a given block. Return SCALE encoded ApplyExtrinsicResult.
Future<Uint8List> dryRun(Uint8List extrinsic, {BlockHash? at}) async {
final List<String> params = ['0x${hex.encode(extrinsic)}'];
if (at != null) {
params.add('0x${hex.encode(at)}');
}
final response = await _provider.send('system_dryRun', params);
final data = response.result as String;
return Uint8List.fromList(hex.decode(data.substring(2)));
}

/// Returns the roles the node is running as.
Future<List<String>> nodeRoles() async {
final response = await _provider.send('system_nodeRoles', []);
return (response.result as List).cast<String>().toList();
}

/// Get a custom set of properties as a JSON object, defined in the chain spec.
Future<Map<String, dynamic>> properties() async {
final response = await _provider.send('system_properties', []);
return response.result as Map<String, dynamic>;
}

/// Adds a reserved peer. Returns the empty string or an error. The string
/// parameter should encode a `p2p` multiaddr.
///
/// `/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV`
/// is an example of a valid, passing multiaddr with PeerId attached.
///
/// unsafe: This method is only active with appropriate flags
Future<String?> addReservedPeer(String peer) async {
final response = await _provider.send('system_addReservedPeer', [peer]);
return response.result as String?;
}

/// Remove a reserved peer. Returns the empty string or an error. The string
/// should encode only the PeerId e.g. `QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV`.
///
/// unsafe: This method is only active with appropriate flags
Future<String?> removeReservedPeer(String peerId) async {
final response =
await _provider.send('system_removeReservedPeer', [peerId]);
return response.result as String?;
}

/// Returns the list of reserved peers
Future<List<String>> reservedPeers() async {
final response = await _provider.send('system_reservedPeers', []);
return (response.result as List).cast<String>().toList();
}

/// Adds the supplied directives to the current log filter
///
/// unsafe: This method is only active with appropriate flags
Future<dynamic> addLogFilter(String directives) async {
final response = await _provider.send('system_addLogFilter', [directives]);
return response.result;
}

/// Resets the log filter to Substrate defaults
///
/// unsafe: This method is only active with appropriate flags
Future<dynamic> resetLogFilter() async {
final response = await _provider.send('system_resetLogFilter', []);
return response.result;
}

/// Returns the state of the syncing of the node: starting block, current best block, highest
/// known block.
Future<SyncState> syncState() async {
final response = await _provider.send('system_syncState', []);
final data = response.result as Map<String, dynamic>;
return SyncState.fromJson(data);
}
}
10 changes: 10 additions & 0 deletions packages/polkadart/lib/primitives/api_version.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ class ApiVersion {
'0x${id.toRadixString(16)}',
version,
];

@override
bool operator ==(Object other) =>
other is ApiVersion &&
other.runtimeType == runtimeType &&
other.id == id &&
other.version == version;

@override
int get hashCode => Object.hash(id, version);
}

class $ApiVersionCodec with Codec<ApiVersion> {
Expand Down
Loading

0 comments on commit 3b8a154

Please sign in to comment.