diff --git a/dart/packages/lbry/.gitignore b/dart/packages/lbry/.gitignore new file mode 100644 index 000000000..3d64647b5 --- /dev/null +++ b/dart/packages/lbry/.gitignore @@ -0,0 +1,9 @@ +# Files and directories created by pub +.dart_tool/ +.packages + +# Conventional directory for build outputs +build/ + +# Directory created by dartdoc +doc/api/ diff --git a/dart/packages/lbry/CHANGELOG.md b/dart/packages/lbry/CHANGELOG.md new file mode 100644 index 000000000..687440bac --- /dev/null +++ b/dart/packages/lbry/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version, created by Stagehand diff --git a/dart/packages/lbry/README.md b/dart/packages/lbry/README.md new file mode 100644 index 000000000..5c0ff1373 --- /dev/null +++ b/dart/packages/lbry/README.md @@ -0,0 +1 @@ +LBRY SDK in Dart. diff --git a/dart/packages/lbry/analysis_options.yaml b/dart/packages/lbry/analysis_options.yaml new file mode 100644 index 000000000..a686c1b45 --- /dev/null +++ b/dart/packages/lbry/analysis_options.yaml @@ -0,0 +1,14 @@ +# Defines a default set of lint rules enforced for +# projects at Google. For details and rationale, +# see https://github.com/dart-lang/pedantic#enabled-lints. +include: package:pedantic/analysis_options.yaml + +# For lint rules and documentation, see http://dart-lang.github.io/linter/lints. +# Uncomment to specify additional rules. +# linter: +# rules: +# - camel_case_types + +analyzer: +# exclude: +# - path/to/excluded/files/** diff --git a/dart/packages/lbry/bin/main.dart b/dart/packages/lbry/bin/main.dart new file mode 100644 index 000000000..4ebbe060d --- /dev/null +++ b/dart/packages/lbry/bin/main.dart @@ -0,0 +1,5 @@ +import 'package:lbry/src/load_generator.dart' as load; + +main(List arguments) { + load.cli(); +} diff --git a/dart/packages/lbry/lib/lbry.dart b/dart/packages/lbry/lib/lbry.dart new file mode 100644 index 000000000..ac03f010c --- /dev/null +++ b/dart/packages/lbry/lib/lbry.dart @@ -0,0 +1 @@ +export 'src/load_generator.dart' show LoadGenerator, LoadDataPoint; diff --git a/dart/packages/lbry/lib/src/load_generator.dart b/dart/packages/lbry/lib/src/load_generator.dart new file mode 100644 index 000000000..79ead0f6c --- /dev/null +++ b/dart/packages/lbry/lib/src/load_generator.dart @@ -0,0 +1,146 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:convert'; + + +class LoadRequest { + + static const RESOLVE = { + 'id': 1, + 'method': 'blockchain.claimtrie.resolve', + 'params': ['one', 'two', 'three'] + }; + + static const CLAIM_SEARCH = { + 'id': 1, + 'method': 'blockchain.claimtrie.search', + 'params': { + 'fee_amount': '<1', + 'all_tags': ['funny'], + 'any_tags': [ + 'crypto', + 'outdoors', + 'cars', + 'automotive' + ] + } + }; + + final Map payload; + Completer completer; + Stopwatch timer; + + bool get isDone => completer.isCompleted; + int get elapsed => timer.elapsedMilliseconds; + + LoadRequest.search(): payload=CLAIM_SEARCH; + LoadRequest.resolve(): payload=RESOLVE; + + LoadRequest start() { + completer = Completer(); + timer = Stopwatch()..start(); + completer.future.whenComplete(() => timer.stop()); + try { + Socket.connect('127.0.0.1', 50001).then((socket) { + socket.transform(utf8.decoder).listen((r) { + if (r.contains('"jsonrpc": "2.0", "result": ')) { + socket.close(); + completer.complete(); + } + }, onError: (e) {print(e); completer.complete();}); + try { + socket.write(jsonEncode(payload) + '\n'); + } catch (exception, stackTrace) { + print(exception); + print(stackTrace); + completer.complete(); + } + }, onError: (e) {print(e);completer.complete();}); + } catch (exception, stackTrace) { + print(exception); + print(stackTrace); + completer.complete(); + } + return this; + } +} + +typedef bool LoadTestCallback(LoadGenerator load_generator, LoadDataPoint stats); + +class LoadGenerator { + int load = 1; + Timer _timer; + + LoadTestCallback cb; + + LoadGenerator(this.cb); + + start() { + var previous = spawn_requests(); + var backlog = []; + _timer = Timer.periodic(Duration(seconds: 1), (t) { + var stat = LoadDataPoint(); + backlog.removeWhere((r) { + if (r.isDone) stat.addCatchup(r); + return r.isDone; + }); + for (var f in previous) { + if (f.isDone) { + stat.addSuccess(f); + } else { + backlog.add(f); + } + } + stat.backlog = backlog.length; + if (cb(this, stat)) { + previous = spawn_requests(); + } else { + t.cancel(); + } + }); + } + + stop() { + _timer.cancel(); + } + + List spawn_requests() { + var requests = []; + for (var _ in Iterable.generate(load)) { + requests.add(LoadRequest.resolve().start()); + } + return requests; + } + +} + +class LoadDataPoint { + final DateTime time = new DateTime.now(); + int success = 0; + int errored = 0; + int backlog = 0; + int catchup = 0; + int _success_total = 0; + int _catchup_total = 0; + int load = 0; + + int get avg_success => _success_total > 0 ? (_success_total/success).round() : 0; + int get avg_catchup => _catchup_total > 0 ? (_catchup_total/catchup).round() : 0; + + addSuccess(LoadRequest r) { + success++; _success_total += r.elapsed; + } + + addCatchup(LoadRequest r) { + catchup++; _catchup_total += r.elapsed; + } +} + +cli() { + var runs = 1; + LoadGenerator((t, stats) { + print("run ${runs}: ${stats}"); + t.load = (runs < 4 ? t.load*2 : t.load/2).round(); + return runs++ < 10; + }).start(); +} diff --git a/dart/packages/lbry/lib/src/schema/Makefile b/dart/packages/lbry/lib/src/schema/Makefile new file mode 100644 index 000000000..f67573f64 --- /dev/null +++ b/dart/packages/lbry/lib/src/schema/Makefile @@ -0,0 +1,2 @@ +build: + protoc --dart_out=v2 -I ../../../../../../../types/v2/proto/ ../../../../../../../types/v2/proto/*.proto diff --git a/dart/packages/lbry/lib/src/schema/v2/claim.pb.dart b/dart/packages/lbry/lib/src/schema/v2/claim.pb.dart new file mode 100644 index 000000000..224bab0b9 --- /dev/null +++ b/dart/packages/lbry/lib/src/schema/v2/claim.pb.dart @@ -0,0 +1,625 @@ +/// +// Generated code. Do not modify. +// source: claim.proto +/// +// ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type + +import 'dart:core' as $core show bool, Deprecated, double, int, List, Map, override, pragma, String; + +import 'package:fixnum/fixnum.dart'; +import 'package:protobuf/protobuf.dart' as $pb; + +import 'claim.pbenum.dart'; + +export 'claim.pbenum.dart'; + +enum Claim_Type { + stream, + channel, + collection, + repost, + notSet +} + +class Claim extends $pb.GeneratedMessage { + static const $core.Map<$core.int, Claim_Type> _Claim_TypeByTag = { + 1 : Claim_Type.stream, + 2 : Claim_Type.channel, + 3 : Claim_Type.collection, + 4 : Claim_Type.repost, + 0 : Claim_Type.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo('Claim', package: const $pb.PackageName('pb')) + ..oo(0, [1, 2, 3, 4]) + ..a(1, 'stream', $pb.PbFieldType.OM, Stream.getDefault, Stream.create) + ..a(2, 'channel', $pb.PbFieldType.OM, Channel.getDefault, Channel.create) + ..a(3, 'collection', $pb.PbFieldType.OM, ClaimList.getDefault, ClaimList.create) + ..a(4, 'repost', $pb.PbFieldType.OM, ClaimReference.getDefault, ClaimReference.create) + ..aOS(8, 'title') + ..aOS(9, 'description') + ..a(10, 'thumbnail', $pb.PbFieldType.OM, Source.getDefault, Source.create) + ..pPS(11, 'tags') + ..pc(12, 'languages', $pb.PbFieldType.PM,Language.create) + ..pc(13, 'locations', $pb.PbFieldType.PM,Location.create) + ..hasRequiredFields = false + ; + + Claim._() : super(); + factory Claim() => create(); + factory Claim.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory Claim.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + Claim clone() => Claim()..mergeFromMessage(this); + Claim copyWith(void Function(Claim) updates) => super.copyWith((message) => updates(message as Claim)); + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static Claim create() => Claim._(); + Claim createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + static Claim getDefault() => _defaultInstance ??= create()..freeze(); + static Claim _defaultInstance; + + Claim_Type whichType() => _Claim_TypeByTag[$_whichOneof(0)]; + void clearType() => clearField($_whichOneof(0)); + + Stream get stream => $_getN(0); + set stream(Stream v) { setField(1, v); } + $core.bool hasStream() => $_has(0); + void clearStream() => clearField(1); + + Channel get channel => $_getN(1); + set channel(Channel v) { setField(2, v); } + $core.bool hasChannel() => $_has(1); + void clearChannel() => clearField(2); + + ClaimList get collection => $_getN(2); + set collection(ClaimList v) { setField(3, v); } + $core.bool hasCollection() => $_has(2); + void clearCollection() => clearField(3); + + ClaimReference get repost => $_getN(3); + set repost(ClaimReference v) { setField(4, v); } + $core.bool hasRepost() => $_has(3); + void clearRepost() => clearField(4); + + $core.String get title => $_getS(4, ''); + set title($core.String v) { $_setString(4, v); } + $core.bool hasTitle() => $_has(4); + void clearTitle() => clearField(8); + + $core.String get description => $_getS(5, ''); + set description($core.String v) { $_setString(5, v); } + $core.bool hasDescription() => $_has(5); + void clearDescription() => clearField(9); + + Source get thumbnail => $_getN(6); + set thumbnail(Source v) { setField(10, v); } + $core.bool hasThumbnail() => $_has(6); + void clearThumbnail() => clearField(10); + + $core.List<$core.String> get tags => $_getList(7); + + $core.List get languages => $_getList(8); + + $core.List get locations => $_getList(9); +} + +enum Stream_Type { + image, + video, + audio, + software, + notSet +} + +class Stream extends $pb.GeneratedMessage { + static const $core.Map<$core.int, Stream_Type> _Stream_TypeByTag = { + 10 : Stream_Type.image, + 11 : Stream_Type.video, + 12 : Stream_Type.audio, + 13 : Stream_Type.software, + 0 : Stream_Type.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo('Stream', package: const $pb.PackageName('pb')) + ..oo(0, [10, 11, 12, 13]) + ..a(1, 'source', $pb.PbFieldType.OM, Source.getDefault, Source.create) + ..aOS(2, 'author') + ..aOS(3, 'license') + ..aOS(4, 'licenseUrl') + ..aInt64(5, 'releaseTime') + ..a(6, 'fee', $pb.PbFieldType.OM, Fee.getDefault, Fee.create) + ..a(10, 'image', $pb.PbFieldType.OM, Image.getDefault, Image.create) + ..a