1641323880
V tomto příspěvku se dozvíme, co jsou peněženky s více podpisy v kryptoměnách a jak to funguje?
Hlavním problémem pro držitele kryptoměn je bezpečnost. Obavy o bezpečnost se často týkají bezpečnostních protokolů a zabezpečení kryptoměnových peněženek, které představují významnou hrozbu pro finanční prostředky, které mohou být zmanipulovány nebo ukradeny hackery.
Jedním z řešení, jak zabránit ztrátě, je peněženka s více podpisy. Tento článek podrobně popisuje vše, co potřebujete vědět o multisig peněženkách, včetně jejich původu, jak to funguje, případů použití a také výhod a nevýhod.
Peněženka s více podpisy (zkráceně „multisig“) je kryptoměnová peněženka, která vyžaduje dva nebo více soukromých klíčů k podepsání a odeslání transakce. Tento typ digitálního podpisu umožňuje dvěma nebo více uživatelům podepisovat dokumenty jako skupina. Spoluvlastníci a signatáři sdílené multisig peněženky jsou známí jako „copayers“.
Počet podpisů potřebných k podpisu transakce závisí na druhu peněženky. Může být nižší nebo roven počtu kopitorů peněženky.
Technologie Multisig existovala ve světě kryptoměn, ale samotný princip existoval dávno předtím, než byl vůbec vytvořen Bitcoin.
SOUVISEJÍCÍ: Co je to kryptoměna?
V roce 2012 byla tato technologie poprvé představena a aplikována na bitcoinový blockchain . O rok později, v roce 2013, byly na trh uvedeny první multisig peněženky vytvořené pro koncového uživatele. Předtím byla normou pro osobní uživatele jediná klíčová adresa.
Multisig peněženky fungují podobným způsobem jako bankovní trezory. Technická součást za bankovním trezorem umožňuje, že k jeho otevření je zapotřebí více než jeden klíč. V důsledku toho se multisig peněženkám často říká vaulty.
Můžete si vybrat, kolik klíčů smí otevřít „trezor“ (stejně jako minimální počet klíčů potřebných k jeho odemknutí). Můžete se například rozhodnout mít multisig 3 ze 4, kde jsou potřeba tři ze čtyř přiřazených soukromých klíčů.
Peněženky s více podpisy nabízejí další funkce, které nabízejí nekrypto peněženky, jako je poskytování přístupu každému plátci a dohled nad finančními prostředky a transakcemi jejich peněženek.
Každému plátci sdílejícímu peněženku je přidělena jedinečná obnovovací fráze. Poplatníci musí zachovat svou frázi pro obnovení v bezpečí, jinak riskují, že nebude dostatek plátců k podpisu transakcí.
S multisig peněženkami je spojeno mnoho výhod.
Přestože jsou multisig peněženky dobrým řešením pro různé problémy, stále existují určitá rizika a omezení, která je třeba mít na paměti:
Multisig adresy lze použít pro různé okolnosti, ale většina se týká bezpečnosti.
Použití multisig peněženky umožňuje uživatelům vytvořit další vrstvu zabezpečení pro své prostředky. Pokud je jeden z klíčů kompromitován, uživatel si může být jistý, že jeho prostředky jsou stále v bezpečí.
Technologie Multisig také zabraňuje malwarovým infekcím a phishingovým útokům, protože hacker má pravděpodobně přístup pouze k jedinému klíči nebo zařízení.
Představte si obchodní partnerství mezi několika jednotlivci. Multisig peněženka může být použita ke kontrole přístupu k jejich společným firemním fondům.
Mohou se rozhodnout založit peněženku 4 ze 6, kde každý jednotlivec drží jeden klíč a žádný z nich nemůže prostředky zneužít nebo získat přístup. To znamená, že budou přijímána pouze rozhodnutí, která budou jednomyslně schválena většinou.
Peněženka 2-z-3 multisig může umožnit escrow transakce mezi dvěma stranami (A a B). Transakce také zahrnuje třetí stranu (C) jako vzájemně důvěryhodného arbitra, pokud se něco pokazí.
Multisig se také používá jako forma dvoufaktorové autentizace. Je to proto, že soukromé klíče mohou být uloženy na různých zařízeních.
Při použití technologie multisig jako dvoufaktorové autentizace však existují rizika. Rizika se zdvojnásobí, pokud je adresa multisig nastavena na soukromé klíče 2 ze 2. Pokud dojde ke ztrátě jednoho z klíčů, dojde také ke ztrátě přístupu k prostředkům.
Normálně jsou kryptoměny uloženy na standardní adrese s jedním klíčem, což znamená, že kdokoli vlastní správný soukromý klíč, získá přístup k prostředkům. To znamená, že k podepisování transakcí a převodu mincí dle libosti bez další autorizace je potřeba pouze jeden klíč.
Ačkoli je použití adresy s jedním klíčem rychlejší a jednodušší než vícesměrová adresa, představuje několik problémů, zejména pokud jde o bezpečnost. Například s jedním klíčem jsou prostředky chráněny pouze jediným bodem selhání.
Kyberzločinci neustále vyvíjejí nové phishingové techniky, jak ukrást finanční prostředky uživatelů kryptoměn.
Po zvážení výhod a nevýhod jsou peněženky s více podpisy mnohem důvěryhodnější a vysoce bezpečnou alternativou pro správu kryptofondů. Pokud jsou peněženky multisig správně používány, poskytují řadu užitečných aplikací, díky nimž jsou bitcoiny a další kryptoměny atraktivnější, užitečnější a bezpečnější.
Vyžadováním více než jednoho podpisu pro převod finančních prostředků poskytují multisig peněženky zvýšenou bezpečnost a umožňují jednomyslné rozhodování. Není pochyb o tom, že technologie pravděpodobně zaznamená v budoucnu zvýšené využití a zavedení dalších užitečných aktualizací.
Mohu být podveden multisig peněženkou?
Ano, útočníci mohou použít multisig peněženku k oklamání oběti.
Podvod funguje takto:
Naše doporučení:
- Pokud kupujete kryptoměnu, ujistěte se, že peněženka, do které budete dostávat prostředky, je peněženka, kterou vlastníte, peněženka, kterou jste vytvořili, není multisig peněženka a máte k ní přístup pouze vy.
- Pokud kupujete kryptoměnu, dejte si pozor na ceny „příliš dobré, aby to byla pravda“ nebo na někoho, kdo nabízí kryptoměnu za mnohem nižší cenu, než je aktuální kurz.
Pokud vám někdo řekne, abyste vytvořili nebo se připojili k multisig peněžence, aby vám poslali peníze, snaží se vás podvést.
Otázka: Chci ke svým prostředkům přidat zabezpečení, je multisig peněženka dobrá volba?
Odpověď: Protože multisig peněženka vyžaduje více podpisů k podepsání transakce, zvyšuje bezpečnost vaší peněženky. Pokud by útočník získal zařízení jednoho hráče, nemohl by utratit prostředky vaší peněženky, pokud požadujete alespoň dva podpisy. Mějte však na paměti následující:
Otázka: Chci sdílet peněženku se svým manželem, co doporučujete?
A: Existuje mnoho multisig konfigurací pro dva copayers:
Otázka: Mám peněženku 2-2. Druhý plátce ztratil své zařízení a nemá frázi pro obnovení. Jak můžeme získat zpět peněženku?
Odpověď: V tomto případě bohužel neexistuje způsob, jak peněženku získat zpět. K obnovení peněženky jsou vyžadovány obě obnovovací fráze.
Otázka: Mám peněženku 2-2. Druhý plátce ztratil své zařízení a nemá frázi pro obnovení, ale peněženku mám nastavenou ve dvou zařízeních. Proč nemohu přesouvat finanční prostředky?
Odpověď: Každý plátce má jiný a jedinečný podpis, který musí být poskytnut, aby bylo možné přesunout finanční prostředky. To znamená, že duplikování stejné peněženky copayer na mnoha zařízeních nepomůže, protože poskytují stejný podpis. Pokud ztratíte jakoukoli frázi pro obnovení pro multisig peněženku, ve které potřebujete každý podpis (například 2-2), ztratíte přístup k prostředkům v peněžence.
Otázka: Platím vyšší poplatek za použití multisig peněženky k odesílání prostředků?
A: Ano. Skriptování je odlišné a transakce obsahuje více podpisů. Tyto dva faktory vedou k větší transakci (v bajtech), která vyžaduje vyšší celkový poplatek.
1656151740
Flutter Console Coverage Test
This small dart tools is used to generate Flutter Coverage Test report to console
Add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):
dev_dependencies:
test_cov_console: ^0.2.2
flutter pub get
Running "flutter pub get" in coverage... 0.5s
flutter test --coverage
00:02 +1: All tests passed!
flutter pub run test_cov_console
---------------------------------------------|---------|---------|---------|-------------------|
File |% Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------------------------------|---------|---------|---------|-------------------|
lib/src/ | | | | |
print_cov.dart | 100.00 | 100.00 | 88.37 |...,149,205,206,207|
print_cov_constants.dart | 0.00 | 0.00 | 0.00 | no unit testing|
lib/ | | | | |
test_cov_console.dart | 0.00 | 0.00 | 0.00 | no unit testing|
---------------------------------------------|---------|---------|---------|-------------------|
All files with unit testing | 100.00 | 100.00 | 88.37 | |
---------------------------------------------|---------|---------|---------|-------------------|
If not given a FILE, "coverage/lcov.info" will be used.
-f, --file=<FILE> The target lcov.info file to be reported
-e, --exclude=<STRING1,STRING2,...> A list of contains string for files without unit testing
to be excluded from report
-l, --line It will print Lines & Uncovered Lines only
Branch & Functions coverage percentage will not be printed
-i, --ignore It will not print any file without unit testing
-m, --multi Report from multiple lcov.info files
-c, --csv Output to CSV file
-o, --output=<CSV-FILE> Full path of output CSV file
If not given, "coverage/test_cov_console.csv" will be used
-t, --total Print only the total coverage
Note: it will ignore all other option (if any), except -m
-p, --pass=<MINIMUM> Print only the whether total coverage is passed MINIMUM value or not
If the value >= MINIMUM, it will print PASSED, otherwise FAILED
Note: it will ignore all other option (if any), except -m
-h, --help Show this help
flutter pub run test_cov_console --file=coverage/lcov.info --exclude=_constants,_mock
---------------------------------------------|---------|---------|---------|-------------------|
File |% Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------------------------------|---------|---------|---------|-------------------|
lib/src/ | | | | |
print_cov.dart | 100.00 | 100.00 | 88.37 |...,149,205,206,207|
lib/ | | | | |
test_cov_console.dart | 0.00 | 0.00 | 0.00 | no unit testing|
---------------------------------------------|---------|---------|---------|-------------------|
All files with unit testing | 100.00 | 100.00 | 88.37 | |
---------------------------------------------|---------|---------|---------|-------------------|
It support to run for multiple lcov.info files with the followings directory structures:
1. No root module
<root>/<module_a>
<root>/<module_a>/coverage/lcov.info
<root>/<module_a>/lib/src
<root>/<module_b>
<root>/<module_b>/coverage/lcov.info
<root>/<module_b>/lib/src
...
2. With root module
<root>/coverage/lcov.info
<root>/lib/src
<root>/<module_a>
<root>/<module_a>/coverage/lcov.info
<root>/<module_a>/lib/src
<root>/<module_b>
<root>/<module_b>/coverage/lcov.info
<root>/<module_b>/lib/src
...
You must run test_cov_console on <root> dir, and the report would be grouped by module, here is
the sample output for directory structure 'with root module':
flutter pub run test_cov_console --file=coverage/lcov.info --exclude=_constants,_mock --multi
---------------------------------------------|---------|---------|---------|-------------------|
File |% Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------------------------------|---------|---------|---------|-------------------|
lib/src/ | | | | |
print_cov.dart | 100.00 | 100.00 | 88.37 |...,149,205,206,207|
lib/ | | | | |
test_cov_console.dart | 0.00 | 0.00 | 0.00 | no unit testing|
---------------------------------------------|---------|---------|---------|-------------------|
All files with unit testing | 100.00 | 100.00 | 88.37 | |
---------------------------------------------|---------|---------|---------|-------------------|
---------------------------------------------|---------|---------|---------|-------------------|
File - module_a - |% Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------------------------------|---------|---------|---------|-------------------|
lib/src/ | | | | |
print_cov.dart | 100.00 | 100.00 | 88.37 |...,149,205,206,207|
lib/ | | | | |
test_cov_console.dart | 0.00 | 0.00 | 0.00 | no unit testing|
---------------------------------------------|---------|---------|---------|-------------------|
All files with unit testing | 100.00 | 100.00 | 88.37 | |
---------------------------------------------|---------|---------|---------|-------------------|
---------------------------------------------|---------|---------|---------|-------------------|
File - module_b - |% Branch | % Funcs | % Lines | Uncovered Line #s |
---------------------------------------------|---------|---------|---------|-------------------|
lib/src/ | | | | |
print_cov.dart | 100.00 | 100.00 | 88.37 |...,149,205,206,207|
lib/ | | | | |
test_cov_console.dart | 0.00 | 0.00 | 0.00 | no unit testing|
---------------------------------------------|---------|---------|---------|-------------------|
All files with unit testing | 100.00 | 100.00 | 88.37 | |
---------------------------------------------|---------|---------|---------|-------------------|
flutter pub run test_cov_console -c --output=coverage/test_coverage.csv
#### sample CSV output file:
File,% Branch,% Funcs,% Lines,Uncovered Line #s
lib/,,,,
test_cov_console.dart,0.00,0.00,0.00,no unit testing
lib/src/,,,,
parser.dart,100.00,100.00,97.22,"97"
parser_constants.dart,100.00,100.00,100.00,""
print_cov.dart,100.00,100.00,82.91,"29,49,51,52,171,174,177,180,183,184,185,186,187,188,279,324,325,387,388,389,390,391,392,393,394,395,398"
print_cov_constants.dart,0.00,0.00,0.00,no unit testing
All files with unit testing,100.00,100.00,86.07,""
You can install the package from the command line:
dart pub global activate test_cov_console
The package has the following executables:
$ test_cov_console
Run this command:
With Dart:
$ dart pub add test_cov_console
With Flutter:
$ flutter pub add test_cov_console
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get
):
dependencies:
test_cov_console: ^0.2.2
Alternatively, your editor might support dart pub get
or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:test_cov_console/test_cov_console.dart';
example/lib/main.dart
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
// This makes the visual density adapt to the platform that you run
// the app on. For desktop platforms, the controls will be smaller and
// closer together (more dense) than on mobile platforms.
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Author: DigitalKatalis
Source Code: https://github.com/DigitalKatalis/test_cov_console
License: BSD-3-Clause license
1641323880
V tomto příspěvku se dozvíme, co jsou peněženky s více podpisy v kryptoměnách a jak to funguje?
Hlavním problémem pro držitele kryptoměn je bezpečnost. Obavy o bezpečnost se často týkají bezpečnostních protokolů a zabezpečení kryptoměnových peněženek, které představují významnou hrozbu pro finanční prostředky, které mohou být zmanipulovány nebo ukradeny hackery.
Jedním z řešení, jak zabránit ztrátě, je peněženka s více podpisy. Tento článek podrobně popisuje vše, co potřebujete vědět o multisig peněženkách, včetně jejich původu, jak to funguje, případů použití a také výhod a nevýhod.
Peněženka s více podpisy (zkráceně „multisig“) je kryptoměnová peněženka, která vyžaduje dva nebo více soukromých klíčů k podepsání a odeslání transakce. Tento typ digitálního podpisu umožňuje dvěma nebo více uživatelům podepisovat dokumenty jako skupina. Spoluvlastníci a signatáři sdílené multisig peněženky jsou známí jako „copayers“.
Počet podpisů potřebných k podpisu transakce závisí na druhu peněženky. Může být nižší nebo roven počtu kopitorů peněženky.
Technologie Multisig existovala ve světě kryptoměn, ale samotný princip existoval dávno předtím, než byl vůbec vytvořen Bitcoin.
SOUVISEJÍCÍ: Co je to kryptoměna?
V roce 2012 byla tato technologie poprvé představena a aplikována na bitcoinový blockchain . O rok později, v roce 2013, byly na trh uvedeny první multisig peněženky vytvořené pro koncového uživatele. Předtím byla normou pro osobní uživatele jediná klíčová adresa.
Multisig peněženky fungují podobným způsobem jako bankovní trezory. Technická součást za bankovním trezorem umožňuje, že k jeho otevření je zapotřebí více než jeden klíč. V důsledku toho se multisig peněženkám často říká vaulty.
Můžete si vybrat, kolik klíčů smí otevřít „trezor“ (stejně jako minimální počet klíčů potřebných k jeho odemknutí). Můžete se například rozhodnout mít multisig 3 ze 4, kde jsou potřeba tři ze čtyř přiřazených soukromých klíčů.
Peněženky s více podpisy nabízejí další funkce, které nabízejí nekrypto peněženky, jako je poskytování přístupu každému plátci a dohled nad finančními prostředky a transakcemi jejich peněženek.
Každému plátci sdílejícímu peněženku je přidělena jedinečná obnovovací fráze. Poplatníci musí zachovat svou frázi pro obnovení v bezpečí, jinak riskují, že nebude dostatek plátců k podpisu transakcí.
S multisig peněženkami je spojeno mnoho výhod.
Přestože jsou multisig peněženky dobrým řešením pro různé problémy, stále existují určitá rizika a omezení, která je třeba mít na paměti:
Multisig adresy lze použít pro různé okolnosti, ale většina se týká bezpečnosti.
Použití multisig peněženky umožňuje uživatelům vytvořit další vrstvu zabezpečení pro své prostředky. Pokud je jeden z klíčů kompromitován, uživatel si může být jistý, že jeho prostředky jsou stále v bezpečí.
Technologie Multisig také zabraňuje malwarovým infekcím a phishingovým útokům, protože hacker má pravděpodobně přístup pouze k jedinému klíči nebo zařízení.
Představte si obchodní partnerství mezi několika jednotlivci. Multisig peněženka může být použita ke kontrole přístupu k jejich společným firemním fondům.
Mohou se rozhodnout založit peněženku 4 ze 6, kde každý jednotlivec drží jeden klíč a žádný z nich nemůže prostředky zneužít nebo získat přístup. To znamená, že budou přijímána pouze rozhodnutí, která budou jednomyslně schválena většinou.
Peněženka 2-z-3 multisig může umožnit escrow transakce mezi dvěma stranami (A a B). Transakce také zahrnuje třetí stranu (C) jako vzájemně důvěryhodného arbitra, pokud se něco pokazí.
Multisig se také používá jako forma dvoufaktorové autentizace. Je to proto, že soukromé klíče mohou být uloženy na různých zařízeních.
Při použití technologie multisig jako dvoufaktorové autentizace však existují rizika. Rizika se zdvojnásobí, pokud je adresa multisig nastavena na soukromé klíče 2 ze 2. Pokud dojde ke ztrátě jednoho z klíčů, dojde také ke ztrátě přístupu k prostředkům.
Normálně jsou kryptoměny uloženy na standardní adrese s jedním klíčem, což znamená, že kdokoli vlastní správný soukromý klíč, získá přístup k prostředkům. To znamená, že k podepisování transakcí a převodu mincí dle libosti bez další autorizace je potřeba pouze jeden klíč.
Ačkoli je použití adresy s jedním klíčem rychlejší a jednodušší než vícesměrová adresa, představuje několik problémů, zejména pokud jde o bezpečnost. Například s jedním klíčem jsou prostředky chráněny pouze jediným bodem selhání.
Kyberzločinci neustále vyvíjejí nové phishingové techniky, jak ukrást finanční prostředky uživatelů kryptoměn.
Po zvážení výhod a nevýhod jsou peněženky s více podpisy mnohem důvěryhodnější a vysoce bezpečnou alternativou pro správu kryptofondů. Pokud jsou peněženky multisig správně používány, poskytují řadu užitečných aplikací, díky nimž jsou bitcoiny a další kryptoměny atraktivnější, užitečnější a bezpečnější.
Vyžadováním více než jednoho podpisu pro převod finančních prostředků poskytují multisig peněženky zvýšenou bezpečnost a umožňují jednomyslné rozhodování. Není pochyb o tom, že technologie pravděpodobně zaznamená v budoucnu zvýšené využití a zavedení dalších užitečných aktualizací.
Mohu být podveden multisig peněženkou?
Ano, útočníci mohou použít multisig peněženku k oklamání oběti.
Podvod funguje takto:
Naše doporučení:
- Pokud kupujete kryptoměnu, ujistěte se, že peněženka, do které budete dostávat prostředky, je peněženka, kterou vlastníte, peněženka, kterou jste vytvořili, není multisig peněženka a máte k ní přístup pouze vy.
- Pokud kupujete kryptoměnu, dejte si pozor na ceny „příliš dobré, aby to byla pravda“ nebo na někoho, kdo nabízí kryptoměnu za mnohem nižší cenu, než je aktuální kurz.
Pokud vám někdo řekne, abyste vytvořili nebo se připojili k multisig peněžence, aby vám poslali peníze, snaží se vás podvést.
Otázka: Chci ke svým prostředkům přidat zabezpečení, je multisig peněženka dobrá volba?
Odpověď: Protože multisig peněženka vyžaduje více podpisů k podepsání transakce, zvyšuje bezpečnost vaší peněženky. Pokud by útočník získal zařízení jednoho hráče, nemohl by utratit prostředky vaší peněženky, pokud požadujete alespoň dva podpisy. Mějte však na paměti následující:
Otázka: Chci sdílet peněženku se svým manželem, co doporučujete?
A: Existuje mnoho multisig konfigurací pro dva copayers:
Otázka: Mám peněženku 2-2. Druhý plátce ztratil své zařízení a nemá frázi pro obnovení. Jak můžeme získat zpět peněženku?
Odpověď: V tomto případě bohužel neexistuje způsob, jak peněženku získat zpět. K obnovení peněženky jsou vyžadovány obě obnovovací fráze.
Otázka: Mám peněženku 2-2. Druhý plátce ztratil své zařízení a nemá frázi pro obnovení, ale peněženku mám nastavenou ve dvou zařízeních. Proč nemohu přesouvat finanční prostředky?
Odpověď: Každý plátce má jiný a jedinečný podpis, který musí být poskytnut, aby bylo možné přesunout finanční prostředky. To znamená, že duplikování stejné peněženky copayer na mnoha zařízeních nepomůže, protože poskytují stejný podpis. Pokud ztratíte jakoukoli frázi pro obnovení pro multisig peněženku, ve které potřebujete každý podpis (například 2-2), ztratíte přístup k prostředkům v peněžence.
Otázka: Platím vyšší poplatek za použití multisig peněženky k odesílání prostředků?
A: Ano. Skriptování je odlišné a transakce obsahuje více podpisů. Tyto dva faktory vedou k větší transakci (v bajtech), která vyžaduje vyšší celkový poplatek.
1640144506
In this video we're taking a look at the String, &String and &str types in Rust!
Exercise solutions: https://github.com/PascalPrecht/rustlings/commits/solutions
---
0:00 Intro
0:09 Exercise 1
4:47 Exercise 2
10:38 Outro
There are two types of strings in Rust: String
and &str
.
A String
is stored as a vector of bytes (Vec<u8>
), but guaranteed to always be a valid UTF-8 sequence. String
is heap allocated, growable and not null terminated.
&str
is a slice (&[u8]
) that always points to a valid UTF-8 sequence, and can be used to view into a String
, just like &[T]
is a view into Vec<T>
.
fn main() {
// (all the type annotations are superfluous)
// A reference to a string allocated in read only memory
let pangram: &'static str = "the quick brown fox jumps over the lazy dog";
println!("Pangram: {}", pangram);
// Iterate over words in reverse, no new string is allocated
println!("Words in reverse");
for word in pangram.split_whitespace().rev() {
println!("> {}", word);
}
// Copy chars into a vector, sort and remove duplicates
let mut chars: Vec<char> = pangram.chars().collect();
chars.sort();
chars.dedup();
// Create an empty and growable `String`
let mut string = String::new();
for c in chars {
// Insert a char at the end of string
string.push(c);
// Insert a string at the end of string
string.push_str(", ");
}
// The trimmed string is a slice to the original string, hence no new
// allocation is performed
let chars_to_trim: &[char] = &[' ', ','];
let trimmed_str: &str = string.trim_matches(chars_to_trim);
println!("Used characters: {}", trimmed_str);
// Heap allocate a string
let alice = String::from("I like dogs");
// Allocate new memory and store the modified string there
let bob: String = alice.replace("dog", "cat");
println!("Alice says: {}", alice);
println!("Bob says: {}", bob);
}
More str
/String
methods can be found under the std::str and std::string modules
There are multiple ways to write string literals with special characters in them. All result in a similar &str
so it's best to use the form that is the most convenient to write. Similarly there are multiple ways to write byte string literals, which all result in &[u8; N]
.
Generally special characters are escaped with a backslash character: \
. This way you can add any character to your string, even unprintable ones and ones that you don't know how to type. If you want a literal backslash, escape it with another one: \\
String or character literal delimiters occuring within a literal must be escaped: "\""
, '\''
.
fn main() {
// You can use escapes to write bytes by their hexadecimal values...
let byte_escape = "I'm writing \x52\x75\x73\x74!";
println!("What are you doing\x3F (\\x3F means ?) {}", byte_escape);
// ...or Unicode code points.
let unicode_codepoint = "\u{211D}";
let character_name = "\"DOUBLE-STRUCK CAPITAL R\"";
println!("Unicode character {} (U+211D) is called {}",
unicode_codepoint, character_name );
let long_string = "String literals
can span multiple lines.
The linebreak and indentation here ->\
<- can be escaped too!";
println!("{}", long_string);
}
Sometimes there are just too many characters that need to be escaped or it's just much more convenient to write a string out as-is. This is where raw string literals come into play.
fn main() {
let raw_str = r"Escapes don't work here: \x3F \u{211D}";
println!("{}", raw_str);
// If you need quotes in a raw string, add a pair of #s
let quotes = r#"And then I said: "There is no escape!""#;
println!("{}", quotes);
// If you need "# in your string, just use more #s in the delimiter.
// There is no limit for the number of #s you can use.
let longer_delimiter = r###"A string with "# in it. And even "##!"###;
println!("{}", longer_delimiter);
}
Want a string that's not UTF-8? (Remember, str
and String
must be valid UTF-8). Or maybe you want an array of bytes that's mostly text? Byte strings to the rescue!
use std::str;
fn main() {
// Note that this is not actually a `&str`
let bytestring: &[u8; 21] = b"this is a byte string";
// Byte arrays don't have the `Display` trait, so printing them is a bit limited
println!("A byte string: {:?}", bytestring);
// Byte strings can have byte escapes...
let escaped = b"\x52\x75\x73\x74 as bytes";
// ...but no unicode escapes
// let escaped = b"\u{211D} is not allowed";
println!("Some escaped bytes: {:?}", escaped);
// Raw byte strings work just like raw strings
let raw_bytestring = br"\u{211D} is not escaped here";
println!("{:?}", raw_bytestring);
// Converting a byte array to `str` can fail
if let Ok(my_str) = str::from_utf8(raw_bytestring) {
println!("And the same as text: '{}'", my_str);
}
let _quotes = br#"You can also use "fancier" formatting, \
like with normal raw strings"#;
// Byte strings don't have to be UTF-8
let shift_jis = b"\x82\xe6\x82\xa8\x82\xb1\x82\xbb"; // "ようこそ" in SHIFT-JIS
// But then they can't always be converted to `str`
match str::from_utf8(shift_jis) {
Ok(my_str) => println!("Conversion successful: '{}'", my_str),
Err(e) => println!("Conversion failed: {:?}", e),
};
}
For conversions between character encodings check out the encoding crate.
A more detailed listing of the ways to write string literals and escape characters is given in the 'Tokens' chapter of the Rust Reference.
#rust #programming #developer
1636360749
The std
library provides many custom types which expands drastically on the primitives
. Some of these include:
String
s like: "hello world"
[1, 2, 3]
Option<i32>
Result<i32, i32>
Box<i32>
All values in Rust are stack allocated by default. Values can be boxed (allocated on the heap) by creating a Box<T>
. A box is a smart pointer to a heap allocated value of type T
. When a box goes out of scope, its destructor is called, the inner object is destroyed, and the memory on the heap is freed.
Boxed values can be dereferenced using the *
operator; this removes one layer of indirection.
use std::mem;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
struct Point {
x: f64,
y: f64,
}
// A Rectangle can be specified by where its top left and bottom right
// corners are in space
#[allow(dead_code)]
struct Rectangle {
top_left: Point,
bottom_right: Point,
}
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
fn boxed_origin() -> Box<Point> {
// Allocate this point on the heap, and return a pointer to it
Box::new(Point { x: 0.0, y: 0.0 })
}
fn main() {
// (all the type annotations are superfluous)
// Stack allocated variables
let point: Point = origin();
let rectangle: Rectangle = Rectangle {
top_left: origin(),
bottom_right: Point { x: 3.0, y: -4.0 }
};
// Heap allocated rectangle
let boxed_rectangle: Box<Rectangle> = Box::new(Rectangle {
top_left: origin(),
bottom_right: Point { x: 3.0, y: -4.0 },
});
// The output of functions can be boxed
let boxed_point: Box<Point> = Box::new(origin());
// Double indirection
let box_in_a_box: Box<Box<Point>> = Box::new(boxed_origin());
println!("Point occupies {} bytes on the stack",
mem::size_of_val(&point));
println!("Rectangle occupies {} bytes on the stack",
mem::size_of_val(&rectangle));
// box size == pointer size
println!("Boxed point occupies {} bytes on the stack",
mem::size_of_val(&boxed_point));
println!("Boxed rectangle occupies {} bytes on the stack",
mem::size_of_val(&boxed_rectangle));
println!("Boxed box occupies {} bytes on the stack",
mem::size_of_val(&box_in_a_box));
// Copy the data contained in `boxed_point` into `unboxed_point`
let unboxed_point: Point = *boxed_point;
println!("Unboxed point occupies {} bytes on the stack",
mem::size_of_val(&unboxed_point));
}
Vectors are re-sizable arrays. Like slices, their size is not known at compile time, but they can grow or shrink at any time. A vector is represented using 3 parameters:
The capacity indicates how much memory is reserved for the vector. The vector can grow as long as the length is smaller than the capacity. When this threshold needs to be surpassed, the vector is reallocated with a larger capacity.
fn main() {
// Iterators can be collected into vectors
let collected_iterator: Vec<i32> = (0..10).collect();
println!("Collected (0..10) into: {:?}", collected_iterator);
// The `vec!` macro can be used to initialize a vector
let mut xs = vec![1i32, 2, 3];
println!("Initial vector: {:?}", xs);
// Insert new element at the end of the vector
println!("Push 4 into the vector");
xs.push(4);
println!("Vector: {:?}", xs);
// Error! Immutable vectors can't grow
collected_iterator.push(0);
// FIXME ^ Comment out this line
// The `len` method yields the number of elements currently stored in a vector
println!("Vector length: {}", xs.len());
// Indexing is done using the square brackets (indexing starts at 0)
println!("Second element: {}", xs[1]);
// `pop` removes the last element from the vector and returns it
println!("Pop last element: {:?}", xs.pop());
// Out of bounds indexing yields a panic
println!("Fourth element: {}", xs[3]);
// FIXME ^ Comment out this line
// `Vector`s can be easily iterated over
println!("Contents of xs:");
for x in xs.iter() {
println!("> {}", x);
}
// A `Vector` can also be iterated over while the iteration
// count is enumerated in a separate variable (`i`)
for (i, x) in xs.iter().enumerate() {
println!("In position {} we have value {}", i, x);
}
// Thanks to `iter_mut`, mutable `Vector`s can also be iterated
// over in a way that allows modifying each value
for x in xs.iter_mut() {
*x *= 3;
}
println!("Updated vector: {:?}", xs);
}
More Vec
methods can be found under the std::vec module
There are two types of strings in Rust: String
and &str
.
A String
is stored as a vector of bytes (Vec<u8>
), but guaranteed to always be a valid UTF-8 sequence. String
is heap allocated, growable and not null terminated.
&str
is a slice (&[u8]
) that always points to a valid UTF-8 sequence, and can be used to view into a String
, just like &[T]
is a view into Vec<T>
.
fn main() {
// (all the type annotations are superfluous)
// A reference to a string allocated in read only memory
let pangram: &'static str = "the quick brown fox jumps over the lazy dog";
println!("Pangram: {}", pangram);
// Iterate over words in reverse, no new string is allocated
println!("Words in reverse");
for word in pangram.split_whitespace().rev() {
println!("> {}", word);
}
// Copy chars into a vector, sort and remove duplicates
let mut chars: Vec<char> = pangram.chars().collect();
chars.sort();
chars.dedup();
// Create an empty and growable `String`
let mut string = String::new();
for c in chars {
// Insert a char at the end of string
string.push(c);
// Insert a string at the end of string
string.push_str(", ");
}
// The trimmed string is a slice to the original string, hence no new
// allocation is performed
let chars_to_trim: &[char] = &[' ', ','];
let trimmed_str: &str = string.trim_matches(chars_to_trim);
println!("Used characters: {}", trimmed_str);
// Heap allocate a string
let alice = String::from("I like dogs");
// Allocate new memory and store the modified string there
let bob: String = alice.replace("dog", "cat");
println!("Alice says: {}", alice);
println!("Bob says: {}", bob);
}
More str
/String
methods can be found under the std::str and std::string modules
There are multiple ways to write string literals with special characters in them. All result in a similar &str
so it's best to use the form that is the most convenient to write. Similarly there are multiple ways to write byte string literals, which all result in &[u8; N]
.
Generally special characters are escaped with a backslash character: \
. This way you can add any character to your string, even unprintable ones and ones that you don't know how to type. If you want a literal backslash, escape it with another one: \\
String or character literal delimiters occuring within a literal must be escaped: "\""
, '\''
.
fn main() {
// You can use escapes to write bytes by their hexadecimal values...
let byte_escape = "I'm writing \x52\x75\x73\x74!";
println!("What are you doing\x3F (\\x3F means ?) {}", byte_escape);
// ...or Unicode code points.
let unicode_codepoint = "\u{211D}";
let character_name = "\"DOUBLE-STRUCK CAPITAL R\"";
println!("Unicode character {} (U+211D) is called {}",
unicode_codepoint, character_name );
let long_string = "String literals
can span multiple lines.
The linebreak and indentation here ->\
<- can be escaped too!";
println!("{}", long_string);
}
Sometimes there are just too many characters that need to be escaped or it's just much more convenient to write a string out as-is. This is where raw string literals come into play.
fn main() {
let raw_str = r"Escapes don't work here: \x3F \u{211D}";
println!("{}", raw_str);
// If you need quotes in a raw string, add a pair of #s
let quotes = r#"And then I said: "There is no escape!""#;
println!("{}", quotes);
// If you need "# in your string, just use more #s in the delimiter.
// There is no limit for the number of #s you can use.
let longer_delimiter = r###"A string with "# in it. And even "##!"###;
println!("{}", longer_delimiter);
}
Want a string that's not UTF-8? (Remember, str
and String
must be valid UTF-8). Or maybe you want an array of bytes that's mostly text? Byte strings to the rescue!
use std::str;
fn main() {
// Note that this is not actually a `&str`
let bytestring: &[u8; 21] = b"this is a byte string";
// Byte arrays don't have the `Display` trait, so printing them is a bit limited
println!("A byte string: {:?}", bytestring);
// Byte strings can have byte escapes...
let escaped = b"\x52\x75\x73\x74 as bytes";
// ...but no unicode escapes
// let escaped = b"\u{211D} is not allowed";
println!("Some escaped bytes: {:?}", escaped);
// Raw byte strings work just like raw strings
let raw_bytestring = br"\u{211D} is not escaped here";
println!("{:?}", raw_bytestring);
// Converting a byte array to `str` can fail
if let Ok(my_str) = str::from_utf8(raw_bytestring) {
println!("And the same as text: '{}'", my_str);
}
let _quotes = br#"You can also use "fancier" formatting, \
like with normal raw strings"#;
// Byte strings don't have to be UTF-8
let shift_jis = b"\x82\xe6\x82\xa8\x82\xb1\x82\xbb"; // "ようこそ" in SHIFT-JIS
// But then they can't always be converted to `str`
match str::from_utf8(shift_jis) {
Ok(my_str) => println!("Conversion successful: '{}'", my_str),
Err(e) => println!("Conversion failed: {:?}", e),
};
}
For conversions between character encodings check out the encoding crate.
A more detailed listing of the ways to write string literals and escape characters is given in the 'Tokens' chapter of the Rust Reference.
Option
Sometimes it's desirable to catch the failure of some parts of a program instead of calling panic!
; this can be accomplished using the Option
enum.
The Option<T>
enum has two variants:
None
, to indicate failure or lack of value, andSome(value)
, a tuple struct that wraps a value
with type T
.// An integer division that doesn't `panic!`
fn checked_division(dividend: i32, divisor: i32) -> Option<i32> {
if divisor == 0 {
// Failure is represented as the `None` variant
None
} else {
// Result is wrapped in a `Some` variant
Some(dividend / divisor)
}
}
// This function handles a division that may not succeed
fn try_division(dividend: i32, divisor: i32) {
// `Option` values can be pattern matched, just like other enums
match checked_division(dividend, divisor) {
None => println!("{} / {} failed!", dividend, divisor),
Some(quotient) => {
println!("{} / {} = {}", dividend, divisor, quotient)
},
}
}
fn main() {
try_division(4, 2);
try_division(1, 0);
// Binding `None` to a variable needs to be type annotated
let none: Option<i32> = None;
let _equivalent_none = None::<i32>;
let optional_float = Some(0f32);
// Unwrapping a `Some` variant will extract the value wrapped.
println!("{:?} unwraps to {:?}", optional_float, optional_float.unwrap());
// Unwrapping a `None` variant will `panic!`
println!("{:?} unwraps to {:?}", none, none.unwrap());
}
Result
We've seen that the Option
enum can be used as a return value from functions that may fail, where None
can be returned to indicate failure. However, sometimes it is important to express why an operation failed. To do this we have the Result
enum.
The Result<T, E>
enum has two variants:
Ok(value)
which indicates that the operation succeeded, and wraps the value
returned by the operation. (value
has type T
)Err(why)
, which indicates that the operation failed, and wraps why
, which (hopefully) explains the cause of the failure. (why
has type E
)mod checked {
// Mathematical "errors" we want to catch
#[derive(Debug)]
pub enum MathError {
DivisionByZero,
NonPositiveLogarithm,
NegativeSquareRoot,
}
pub type MathResult = Result<f64, MathError>;
pub fn div(x: f64, y: f64) -> MathResult {
if y == 0.0 {
// This operation would `fail`, instead let's return the reason of
// the failure wrapped in `Err`
Err(MathError::DivisionByZero)
} else {
// This operation is valid, return the result wrapped in `Ok`
Ok(x / y)
}
}
pub fn sqrt(x: f64) -> MathResult {
if x < 0.0 {
Err(MathError::NegativeSquareRoot)
} else {
Ok(x.sqrt())
}
}
pub fn ln(x: f64) -> MathResult {
if x <= 0.0 {
Err(MathError::NonPositiveLogarithm)
} else {
Ok(x.ln())
}
}
}
// `op(x, y)` === `sqrt(ln(x / y))`
fn op(x: f64, y: f64) -> f64 {
// This is a three level match pyramid!
match checked::div(x, y) {
Err(why) => panic!("{:?}", why),
Ok(ratio) => match checked::ln(ratio) {
Err(why) => panic!("{:?}", why),
Ok(ln) => match checked::sqrt(ln) {
Err(why) => panic!("{:?}", why),
Ok(sqrt) => sqrt,
},
},
}
}
fn main() {
// Will this fail?
println!("{}", op(1.0, 10.0));
}
?
Chaining results using match can get pretty untidy; luckily, the ?
operator can be used to make things pretty again. ?
is used at the end of an expression returning a Result
, and is equivalent to a match expression, where the Err(err)
branch expands to an early Err(From::from(err))
, and the Ok(ok)
branch expands to an ok
expression.
mod checked {
#[derive(Debug)]
enum MathError {
DivisionByZero,
NonPositiveLogarithm,
NegativeSquareRoot,
}
type MathResult = Result<f64, MathError>;
fn div(x: f64, y: f64) -> MathResult {
if y == 0.0 {
Err(MathError::DivisionByZero)
} else {
Ok(x / y)
}
}
fn sqrt(x: f64) -> MathResult {
if x < 0.0 {
Err(MathError::NegativeSquareRoot)
} else {
Ok(x.sqrt())
}
}
fn ln(x: f64) -> MathResult {
if x <= 0.0 {
Err(MathError::NonPositiveLogarithm)
} else {
Ok(x.ln())
}
}
// Intermediate function
fn op_(x: f64, y: f64) -> MathResult {
// if `div` "fails", then `DivisionByZero` will be `return`ed
let ratio = div(x, y)?;
// if `ln` "fails", then `NonPositiveLogarithm` will be `return`ed
let ln = ln(ratio)?;
sqrt(ln)
}
pub fn op(x: f64, y: f64) {
match op_(x, y) {
Err(why) => panic!("{}", match why {
MathError::NonPositiveLogarithm
=> "logarithm of non-positive number",
MathError::DivisionByZero
=> "division by zero",
MathError::NegativeSquareRoot
=> "square root of negative number",
}),
Ok(value) => println!("{}", value),
}
}
}
fn main() {
checked::op(1.0, 10.0);
}
Be sure to check the documentation, as there are many methods to map/compose Result
.
panic!
The panic!
macro can be used to generate a panic and start unwinding its stack. While unwinding, the runtime will take care of freeing all the resources owned by the thread by calling the destructor of all its objects.
Since we are dealing with programs with only one thread, panic!
will cause the program to report the panic message and exit.
// Re-implementation of integer division (/)
fn division(dividend: i32, divisor: i32) -> i32 {
if divisor == 0 {
// Division by zero triggers a panic
panic!("division by zero");
} else {
dividend / divisor
}
}
// The `main` task
fn main() {
// Heap allocated integer
let _x = Box::new(0i32);
// This operation will trigger a task failure
division(3, 0);
println!("This point won't be reached!");
// `_x` should get destroyed at this point
}
Let's check that panic!
doesn't leak memory.
$ rustc panic.rs && valgrind ./panic
==4401== Memcheck, a memory error detector
==4401== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==4401== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==4401== Command: ./panic
==4401==
thread '<main>' panicked at 'division by zero', panic.rs:5
==4401==
==4401== HEAP SUMMARY:
==4401== in use at exit: 0 bytes in 0 blocks
==4401== total heap usage: 18 allocs, 18 frees, 1,648 bytes allocated
==4401==
==4401== All heap blocks were freed -- no leaks are possible
==4401==
==4401== For counts of detected and suppressed errors, rerun with: -v
==4401== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Where vectors store values by an integer index, HashMap
s store values by key. HashMap
keys can be booleans, integers, strings, or any other type that implements the Eq
and Hash
traits. More on this in the next section.
Like vectors, HashMap
s are growable, but HashMaps can also shrink themselves when they have excess space. You can create a HashMap with a certain starting capacity using HashMap::with_capacity(uint)
, or use HashMap::new()
to get a HashMap with a default initial capacity (recommended).
use std::collections::HashMap;
fn call(number: &str) -> &str {
match number {
"798-1364" => "We're sorry, the call cannot be completed as dialed.
Please hang up and try again.",
"645-7689" => "Hello, this is Mr. Awesome's Pizza. My name is Fred.
What can I get for you today?",
_ => "Hi! Who is this again?"
}
}
fn main() {
let mut contacts = HashMap::new();
contacts.insert("Daniel", "798-1364");
contacts.insert("Ashley", "645-7689");
contacts.insert("Katie", "435-8291");
contacts.insert("Robert", "956-1745");
// Takes a reference and returns Option<&V>
match contacts.get(&"Daniel") {
Some(&number) => println!("Calling Daniel: {}", call(number)),
_ => println!("Don't have Daniel's number."),
}
// `HashMap::insert()` returns `None`
// if the inserted value is new, `Some(value)` otherwise
contacts.insert("Daniel", "164-6743");
match contacts.get(&"Ashley") {
Some(&number) => println!("Calling Ashley: {}", call(number)),
_ => println!("Don't have Ashley's number."),
}
contacts.remove(&"Ashley");
// `HashMap::iter()` returns an iterator that yields
// (&'a key, &'a value) pairs in arbitrary order.
for (contact, &number) in contacts.iter() {
println!("Calling {}: {}", contact, call(number));
}
}
For more information on how hashing and hash maps (sometimes called hash tables) work, have a look at Hash Table Wikipedia
Any type that implements the Eq
and Hash
traits can be a key in HashMap
. This includes:
bool
(though not very useful since there is only two possible keys)int
, uint
, and all variations thereofString
and &str
(protip: you can have a HashMap
keyed by String
and call .get()
with an &str
)Note that f32
and f64
do not implement Hash
, likely because floating-point precision errors would make using them as hashmap keys horribly error-prone.
All collection classes implement Eq
and Hash
if their contained type also respectively implements Eq
and Hash
. For example, Vec<T>
will implement Hash
if T
implements Hash
.
You can easily implement Eq
and Hash
for a custom type with just one line: #[derive(PartialEq, Eq, Hash)]
The compiler will do the rest. If you want more control over the details, you can implement Eq
and/or Hash
yourself. This guide will not cover the specifics of implementing Hash
.
To play around with using a struct
in HashMap
, let's try making a very simple user logon system:
use std::collections::HashMap;
// Eq requires that you derive PartialEq on the type.
#[derive(PartialEq, Eq, Hash)]
struct Account<'a>{
username: &'a str,
password: &'a str,
}
struct AccountInfo<'a>{
name: &'a str,
email: &'a str,
}
type Accounts<'a> = HashMap<Account<'a>, AccountInfo<'a>>;
fn try_logon<'a>(accounts: &Accounts<'a>,
username: &'a str, password: &'a str){
println!("Username: {}", username);
println!("Password: {}", password);
println!("Attempting logon...");
let logon = Account {
username,
password,
};
match accounts.get(&logon) {
Some(account_info) => {
println!("Successful logon!");
println!("Name: {}", account_info.name);
println!("Email: {}", account_info.email);
},
_ => println!("Login failed!"),
}
}
fn main(){
let mut accounts: Accounts = HashMap::new();
let account = Account {
username: "j.everyman",
password: "password123",
};
let account_info = AccountInfo {
name: "John Everyman",
email: "j.everyman@email.com",
};
accounts.insert(account, account_info);
try_logon(&accounts, "j.everyman", "psasword123");
try_logon(&accounts, "j.everyman", "password123");
}
Consider a HashSet
as a HashMap
where we just care about the keys ( HashSet<T>
is, in actuality, just a wrapper around HashMap<T, ()>
).
"What's the point of that?" you ask. "I could just store the keys in a Vec
."
A HashSet
's unique feature is that it is guaranteed to not have duplicate elements. That's the contract that any set collection fulfills. HashSet
is just one implementation. (see also: BTreeSet
)
If you insert a value that is already present in the HashSet
, (i.e. the new value is equal to the existing and they both have the same hash), then the new value will replace the old.
This is great for when you never want more than one of something, or when you want to know if you've already got something.
But sets can do more than that.
Sets have 4 primary operations (all of the following calls return an iterator):
union
: get all the unique elements in both sets.
difference
: get all the elements that are in the first set but not the second.
intersection
: get all the elements that are only in both sets.
symmetric_difference
: get all the elements that are in one set or the other, but not both.
Try all of these in the following example:
use std::collections::HashSet;
fn main() {
let mut a: HashSet<i32> = vec![1i32, 2, 3].into_iter().collect();
let mut b: HashSet<i32> = vec![2i32, 3, 4].into_iter().collect();
assert!(a.insert(4));
assert!(a.contains(&4));
// `HashSet::insert()` returns false if
// there was a value already present.
assert!(b.insert(4), "Value 4 is already in set B!");
// FIXME ^ Comment out this line
b.insert(5);
// If a collection's element type implements `Debug`,
// then the collection implements `Debug`.
// It usually prints its elements in the format `[elem1, elem2, ...]`
println!("A: {:?}", a);
println!("B: {:?}", b);
// Print [1, 2, 3, 4, 5] in arbitrary order
println!("Union: {:?}", a.union(&b).collect::<Vec<&i32>>());
// This should print [1]
println!("Difference: {:?}", a.difference(&b).collect::<Vec<&i32>>());
// Print [2, 3, 4] in arbitrary order.
println!("Intersection: {:?}", a.intersection(&b).collect::<Vec<&i32>>());
// Print [1, 5]
println!("Symmetric Difference: {:?}",
a.symmetric_difference(&b).collect::<Vec<&i32>>());
}
(Examples are adapted from the documentation.)
Rc
When multiple ownership is needed, Rc
(Reference Counting) can be used. Rc
keeps track of the number of the references which means the number of owners of the value wrapped inside an Rc
.
Reference count of an Rc
increases by 1 whenever an Rc
is cloned, and decreases by 1 whenever one cloned Rc
is dropped out of the scope. When an Rc
's reference count becomes zero, which means there are no owners remained, both the Rc
and the value are all dropped.
Cloning an Rc
never performs a deep copy. Cloning creates just another pointer to the wrapped value, and increments the count.
use std::rc::Rc;
fn main() {
let rc_examples = "Rc examples".to_string();
{
println!("--- rc_a is created ---");
let rc_a: Rc<String> = Rc::new(rc_examples);
println!("Reference Count of rc_a: {}", Rc::strong_count(&rc_a));
{
println!("--- rc_a is cloned to rc_b ---");
let rc_b: Rc<String> = Rc::clone(&rc_a);
println!("Reference Count of rc_b: {}", Rc::strong_count(&rc_b));
println!("Reference Count of rc_a: {}", Rc::strong_count(&rc_a));
// Two `Rc`s are equal if their inner values are equal
println!("rc_a and rc_b are equal: {}", rc_a.eq(&rc_b));
// We can use methods of a value directly
println!("Length of the value inside rc_a: {}", rc_a.len());
println!("Value of rc_b: {}", rc_b);
println!("--- rc_b is dropped out of scope ---");
}
println!("Reference Count of rc_a: {}", Rc::strong_count(&rc_a));
println!("--- rc_a is dropped out of scope ---");
}
// Error! `rc_examples` already moved into `rc_a`
// And when `rc_a` is dropped, `rc_examples` is dropped together
// println!("rc_examples: {}", rc_examples);
// TODO ^ Try uncommenting this line
}
When shared ownership between threads is needed, Arc
(Atomic Reference Counted) can be used. This struct, via the Clone
implementation can create a reference pointer for the location of a value in the memory heap while increasing the reference counter. As it shares ownership between threads, when the last reference pointer to a value is out of scope, the variable is dropped.
fn main() {
use std::sync::Arc;
use std::thread;
// This variable declaration is where its value is specified.
let apple = Arc::new("the same apple");
for _ in 0..10 {
// Here there is no value specification as it is a pointer to a reference
// in the memory heap.
let apple = Arc::clone(&apple);
thread::spawn(move || {
// As Arc was used, threads can be spawned using the value allocated
// in the Arc variable pointer's location.
println!("{:?}", apple);
});
}
}
Original article source at https://doc.rust-lang.org
#rust #programming #developer
1641314640
V tomto příspěvku se dozvíte, co je 51 % útoků v kryptoměnách a jak to funguje?
Decentralizovaná povaha blockchainu a kryptografický algoritmus jsou pro útoky téměř nemožné. Přesto se Ethereum Classic stane obětí tohoto zákeřného jednání s odhadovanou ztrátou 1,1 milionu dolarů za 51% útok. Co je tedy 51% útok a jak k němu dochází?
51% útok (51% útok) je typ infiltrace blockchainu, která může způsobit narušení sítě a nakonec monopolizaci těžby. K tomuto útoku dochází, když těžař, organizace nebo jednotlivá entita získá více než 50% většinovou kontrolu nad hashovací frekvencí nebo výpočetním výkonem běžícím na blockchainové síti.
V důsledku útoku útočníci získali přístup, aby zabránili těžařům v těžbě, zrušili transakce a nakonec utekli s ukradenými mincemi, které jim nikdy nepatřily.
Když je blockchainová síť unesena, útočník by měl dostatečnou těžební sílu k úpravě transakce. To znamená, že objednávkovou transakci lze upravit a všechny těžební činnosti lze zastavit. Přesně tak 51% útočník zruší transakci, která způsobí dvojí útratu v rámci blockchainu.
Řekněme, že útočník utratil 1 BTC výměnou za produkt, takže ve skutečnosti by měl blockchain transakci zaznamenat. 51% útočník však může vyvolat vrácení peněz tím, že zruší transakci, aby vlastnil BTC i produkt.
51% útok funguje tak, že překryje existující síť a převezme nastavené bezpečnostní protokoly. Tyto dopady mohou, ale nemusí být závažné, ale vše závisí na síle útočníků.
Čím větší procento hashovací síly útočníci mají, tím snazší je útok provést. V konečném důsledku jsou škody následnější. Zde je přehled toho, jak 51 % funguje na Blockchainu a Bitcoinu.
Když jedna entita přemůže výpočetní výkon na blockchainu, může být zahájen 51% útok. Když útok pronikne do hashovací síly bitcoinu, jednotlivec může oddálit nové transakce a případně zmanipulovat použití stejné mince vícekrát.
Vzhledem k tomu, že blockchain používá k ověřování transakcí mechanismus konsenzu Proof-of-Work (PoW), tato narušení zpožďují potvrzení a uspořádání bloků v chronologickém pořadí ze strany těžařů. Řekněme tedy, že se sníží výpočetní výkon těžaře, zpozdí se potvrzení transakce, která má být uspořádána v bloku. Síť blockchains je tedy poškozená. Umožňují tak útočníkům řešit rovnice rychleji než těžař.
Výsledkem bylo, že útočník získal kontrolu, aby zvrátil nepotvrzenou transakci a dvakrát utratil minci. Kromě toho útočníci také získávají odměny pro těžaře, které mají kompenzovat těžaře za aktualizaci blockchainu. A to je přesně to, co se stalo Ethereum Classic (ETC) a Bitcoin Cash (BCH).
Ano, 51% útok poškozuje těžaře a narušení blockchainové sítě dokazuje, že zranitelnost existuje. Odborníci si však myslí opak. Ve skutečnosti může být tento útok prospěšný, když je analyzován z různých perspektiv.
Je jasné, že hashovací síla bitcoinu pochází z příspěvku účastníků, kteří sdílejí stejný cíl, a tito jedinci jsou při rozhodování logičtí. Stále však existuje riziko tajných dohod o změně distribuce hashovací síly, když dojde k cenové diskriminaci. Tak mohou z přesunu moci těžit jiní jedinci nebo se alespoň vymanit ze značných ztrát.
Zatímco základní POW může zůstat odolný vůči blockchainu, jako je bitcoin, menší projekty jako ETC jsou jinak. Je to proto, že entita, která plánuje zaútočit na bitcoiny, by potřebovala obrovské množství peněz na shromáždění těžebního zařízení, aby vytvořila dostatečný výpočetní výkon pro těžbu bitcoinu. Takže i bitcoin je pod 51% útokem; manipulace je nerentabilní.
Jakkoli by tento útok mohl být nebezpečný, některá opatření jsou na blockchainu, který není prostupný.
Zde je to, co potřebujete vědět:
Útok 34 % a 51 % představuje stejnou hrozbu pro blockchain a v konečném důsledku kontroluje těžební sílu. Ale to, co tyto dva útoky odlišuje, je to, že 34% útok používá konsensuální algoritmus Tangle manipulací s účetní knihou blockchainu ke schválení nebo neschválení transakce.
Naopak, 51% útok dává útočníkovi plnou kontrolu nad blockchainovou sítí, která by mohla zastavit jakoukoli těžbu nebo efektivně znovu použít coin.
Teoreticky jsou Bitcoin a Ethereum blockchain odolnější vůči 51% útoku než menší projekty. Přesto zůstává mnoho projektů vůči tomuto útoku zranitelných. Zde jsou některé z platforem, které utrpěly tímto útokem.
Podle nedávných zpráv byla krypto síť GRIN zaměřená na soukromí infiltrována 51% útokem. Anonymní entita nedávno 7. listopadu 2020 získala celkem 58,1 % z hash rate sítě, což vedlo k zastavení rychlé výplaty.
Vzhledem k tomu, že těžba ASIC monopolizuje odolnou kryptoměnu, četné 51% útoky na VTC byly překvapením. V říjnu a prosinci 2018 společnost VTC ztratila VTC v hodnotě 100 000 USD kvůli dvojnásobné útratě ze strany subjektu, který získal dostatečný výpočetní výkon od Nice Hash. Výsledkem bylo, že VTC reorganizovalo více než 300 bloků v síti VTC a dalších 600 bloků hard fork.
V květnu 2018 byly BTG utraceny dvakrát, celkem 12 239 BTG (18 milionů $). Nedávno na konci ledna se bitcoin Gold znovu stal obětí 51% útoku a nakonec utrpěl více než 7 000 BTG dvakrát utracených během dvou dnů. Útok byl výsledkem dvou hlubokých reorganizací blockchainu (reorgs), které byly odstraněny.
The ETC blockchain has suffered not one but three 51% attacks within a month. Each of the attacks happened in the month of August 2020, including the first one that took place on the 1st Aug, 6th Aug, and lastly, on the 29th Aug, respectively. This attack has even led crypto exchange Coinbase to halt all ETC deposits and withdrawals.
A 51% attack prey on cryptocurrencies using a consensus algorithm based on Proof-of-Work (PoW). While the best defense to this attack is the use of Proof-of-Stake (PoS) consensus. That’s precisely what Ethereum 2.0 aims to circumvent.
Při použití algoritmu PoS mohou validátoři zmírnit rizika infiltrace zachováním provozních schopností sítě. PoS například pomáhá omezit množství vsazené kryptoměny. Takže i když je útok z 51 procent možný, entita by zpočátku vyžadovala rozdělit velkou částku fiat na kryptoměnu, aby ovládla systém. Když se podíváme blíže na 51% útok, možnost, že cílem bude kryptoměna PoS, je nepravděpodobná, protože ziskovost je strašná.
Nicméně 51% útok je platný a pravděpodobně se bude opakovat, vzhledem k tomu, že v blockchainových kódech je chyba. Když k takové události dojde, útočník může narušit blockchain, aby rychleji produkoval nové bloky a zahájil útok. Celkově se tento útok může opakovat, ale bitcoinový blockchain je mnohem odolnější.
Jakákoli technologie povstání, včetně blockchainu a kryptoměny, může být vystavena nejrůznějším rizikům a zranitelnostem. To je přesně důvod, proč potřebujeme vědět, co je 51% útok.
Zatímco více technologií slibuje obejít tuto nedostatečnost, kybernetická infiltrace je stále nevyhnutelná. Na druhou stranu takové útoky dávají průmyslu a společnostem legitimní důvody k tomu, aby se co nejlépe učili a zlepšovali. Pojďme se tedy podívat, jaká bude budoucnost tohoto neustále se měnícího odvětví.