Flutter区块链签名插件web3_signers的使用

发布于 1周前 作者 yibo5220 来自 Flutter

Flutter区块链签名插件web3_signers的使用

web3_signers 是一个Flutter插件,提供了一个统一接口用于在Dart中对EIP-1271消息进行签名。该插件支持以下几种签名方式:

  • ✅ Passkey 签名
  • ✅ EOA 钱包(基于助记词)
  • ✅ 私钥签名

快速概述

首先需要导入 web3_signers 包:

import 'package:web3_signers/web3_signers.dart';

使用Passkeys

Passkey签名器符合multi-signer-interface,允许您使用设备的Passkeys对负载进行签名。它属于secp256r1类别,并可以使用P256Verifier预编译在链上验证。

final sharedSigner = EthereumAddress.fromHex("0xfD90FAd33ee8b58f32c00aceEad1358e4AFC23f9");
final options =  PassKeysOptions(
          name: "variance", // 替换为您的依赖方名称
          namespace: "variance.space", // 替换为您的依赖方ID(域名)
          residentKey: "required",
          sharedWebauthnSigner: sharedSigner);

final PassKeySigner pkpSigner = PassKeySigner(options: options);

// 注册一个新的passkey
PassKeyPair pkp = await pkpSigner.register("user@variance.space", "test user"); 
// 用户名为 `user@variance.space` 并且是必需的
// 显示名称为 `test user` 并且建议在注册时提供它

如果已经知道用户的credentialIds,可以这样传递knownCredentials

final PassKeySigner pkpSigner = PassKeySigner(
  ...
  knownCredentials: Set<Bytes>.from(<Uint8List>[Uint8List(32), Uint8List(32)])>
);

这使得认证器能够过滤呈现给用户以进行签名操作的passkeys。

注意: credential Id’s 在 passkeyPair 中以原始格式和base64格式返回。

Passkey签名方法

有三种方法可以使用passkey签名器签署负载:

  • 方法1:使用personalSign
final sig = await pkpSigner.personalSign(Uint8List(32));
  • 方法2:使用signToEc
final sig = await pkpSigner.signToEc(Uint8List(32));
  • 方法3:使用signToPasskeySignature
final PassKeySignature sig = await pkpSigner.signToPasskeySignature(Uint8List(32));

对于上述每种方法,如果您有已知凭据,则可以通过传递索引来提示认证器使用特定凭据进行签名。例如:await pkpSigner.signToPasskeySignature(Uint8List(32), 2); // 使用索引为2的已知凭据进行签名

使用私钥

私钥签名器符合multi-signer-interface,是最基本的签名器。

final PrivateKeySigner signer = PrivateKeySigner.createRandom("password");

// 手动实例化私钥签名器
final Random random = Random.secure();
final EthPrivateKey privKey = EthPrivateKey.createRandom(random);
final PrivateKeySigner signer = PrivateKeySigner.create(privKey, "password", random);

// 从加密备份加载
final PrivateKeySigner signer = PrivateKeySigner.fromJson("source", "password");

// 使用任何一种 `personalSign` 或 `signToEc` 方法签署交易
final Uint8List payload = Uint8List(32); // bytes32(0)
final signature = await signer.signToEc(payload);
log("r: ${signature.r}, s: ${signature.s}") // r 和 s 均为 bigint 格式

构建EOA钱包

除了EIP-1271消息之外,web3-signers包还可以用于开发功能齐全的外部拥有账户(EOA)钱包,如Metamask。

// 创建新的EOA钱包
EOAWallet eoaWallet = EOAWallet.createWallet();

// 默认情况下创建一个12个单词短语的签名器,要创建一个24个单词短语的签名器,需要指定它
eoaWallet = EOAWallet.createWallet(WordLength.word_24); // 返回24个单词短语的签名器

// 检索账户种子短语
final mnemonic = eoaWallet.exportMnemonic();

// 从种子短语恢复EOA钱包
eoaWallet = EOAWallet.recoverAccount(mnemonic);

// 生成新的确定性账户
final accountOne = eoaWallet.addAccount(1);

// 导出账户私钥
final accountZer0PrivKey = eoaWallet.exportPrivateKey(0);
final accountOnePrivKey = eoaWallet.exportPrivateKey(1);

// 获取账户地址
String accountZer0Address = eoaWallet.zerothAddress;
// 或者
accountZer0Address = eoaWallet.getAddress();
final accountOneAddress = eoaWallet.getAddress(index: 1);

完整示例Demo

import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:web3_signers/web3_signers.dart';

void main() {
  runApp(Web3Signer());
}

class Web3Signer extends StatefulWidget {
  const Web3Signer({super.key});

  [@override](/user/override)
  State<Web3Signer> createState() => _Web3SignerState();
}

class _Web3SignerState extends State<Web3Signer> {
  final TextEditingController _textField1Controller = TextEditingController();
  final TextEditingController _textField2Controller = TextEditingController();
  final TextEditingController _textField3Controller = TextEditingController();
  final TextEditingController _textField4Controller = TextEditingController();

  static final sharedSigner =
      EthereumAddress.fromHex("0xfD90FAd33ee8b58f32c00aceEad1358e4AFC23f9");
  static final passkeyOpts = PassKeysOptions(
      name: "variance",
      namespace: "variance.space",
      residentKey: "required",
      requireResidentKey: false,
      sharedWebauthnSigner: sharedSigner);
  final PassKeySigner _pkpSigner = PassKeySigner(options: passkeyOpts);

  PassKeyPair? _pkp;

  void _auth() async {
    _pkp = await _pkpSigner.register("user@variance.space", "test user");
    _updatePkpPublicKey();
  }

  void _signin() async {
    if (_pkp != null) {
      print("credentialIds: ${_pkp!.authData.b64Credential}");
      _pkpSigner.credentialIds.add(_pkp!.authData.rawCredential);
    }
    final sig = await _pkpSigner.personalSign(Uint8List(32));
    final calldata = hexlify(sig);
    _updatePkpSignature(calldata);
  }

  void _updatePkpPublicKey() => setState(() {
        _textField1Controller.text = _pkp!.authData.publicKey.item1.toHex();
        _textField2Controller.text = _pkp!.authData.publicKey.item2.toHex();
      });

  void _updatePkpSignature(String calldata) => setState(() {
        _textField3Controller.text = calldata;
      });

  void _clearAllFields() => setState(() {
        _textField1Controller.clear();
        _textField2Controller.clear();
        _textField3Controller.clear();
        _textField4Controller.clear();
      });

  void _getDummySig() => setState(() {
        final dummy = _pkpSigner.getDummySignature();
        log(dummy);
        _textField4Controller.text = dummy;
      });

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text(
            'Web3 Signer',
          ),
        ),
        body: Padding(
          padding: const EdgeInsets.all(20.0),
          child: Column(
            children: [
              Row(
                crossAxisAlignment: CrossAxisAlignment.end,
                children: [
                  const Text(
                    'PublicKeyX:',
                    style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
                  ),
                  const SizedBox(width: 10.0),
                  Expanded(
                    child: TextField(
                      controller: _textField1Controller,
                      decoration: const InputDecoration(),
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 10.0),
              Row(
                crossAxisAlignment: CrossAxisAlignment.end,
                children: [
                  const Text(
                    'PublicKeyY:',
                    style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
                  ),
                  Expanded(
                    child: TextField(
                      controller: _textField2Controller,
                      decoration: const InputDecoration(),
                    ),
                  ),
                ],
              ),
              Row(
                crossAxisAlignment: CrossAxisAlignment.end,
                children: [
                  const Text(
                    'Signature:',
                    style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
                  ),
                  Expanded(
                    child: TextField(
                      controller: _textField3Controller,
                      decoration: const InputDecoration(),
                      onChanged: (value) {
                        //检测用户输入
                      },
                      onSubmitted: (value) {
                        //当用户表示他们完成编辑文本框时
                      },
                    ),
                  ),
                ],
              ),
              Row(
                crossAxisAlignment: CrossAxisAlignment.end,
                children: [
                  const Text(
                    'Dummy:',
                    style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
                  ),
                  Expanded(
                    child: TextField(
                      controller: _textField4Controller,
                      decoration: const InputDecoration(),
                      onChanged: (value) {
                        //检测用户输入
                      },
                      onSubmitted: (value) {
                        //当用户表示他们完成编辑文本框时
                      },
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 100.0),
              Row(
                children: [
                  Expanded(
                    child: ElevatedButton(
                      style: ElevatedButton.styleFrom(
                        elevation: 1,
                        padding: const EdgeInsets.all(20),
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(15),
                        ),
                      ),
                      onPressed: _auth,
                      child: const Text('Register with Passkey'),
                    ),
                  ),
                  const SizedBox(
                    width: 50,
                  ),
                  Expanded(
                    child: ElevatedButton(
                      style: ElevatedButton.styleFrom(
                        elevation: 1,
                        padding: const EdgeInsets.all(20),
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(15),
                        ),
                      ),
                      onPressed: _signin,
                      child: const Text('Sign with Passkey'),
                    ),
                  ),
                ],
              ),
              const SizedBox(
                height: 20,
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  ElevatedButton(
                    style: ElevatedButton.styleFrom(
                      elevation: 1,
                      padding: const EdgeInsets.all(20),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(15),
                      ),
                    ),
                    onPressed: _clearAllFields,
                    child: const Text('Clear fields'),
                  ),
                  ElevatedButton(
                    style: ElevatedButton.styleFrom(
                      elevation: 1,
                      padding: const EdgeInsets.all(20),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(15),
                      ),
                    ),
                    onPressed: _getDummySig,
                    child: const Text('Get dummy Signature'),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

更多关于Flutter区块链签名插件web3_signers的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter区块链签名插件web3_signers的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用web3_signers插件进行区块链签名的代码示例。web3_signers插件允许你使用私钥对交易或消息进行签名,这在与以太坊区块链交互时非常有用。

首先,确保你的Flutter项目已经添加了对web3dartweb3_signers的依赖。在pubspec.yaml文件中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  web3dart: ^2.0.0  # 请检查最新版本号
  web3_signers: ^1.0.0  # 请检查最新版本号

然后运行flutter pub get来获取这些依赖。

接下来,我们编写一个Flutter应用,它使用web3_signers插件来签名一个消息。

import 'package:flutter/material.dart';
import 'package:web3dart/web3dart.dart';
import 'package:web3_signers/web3_signers.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Blockchain Signing Example'),
        ),
        body: Center(
          child: SignMessageScreen(),
        ),
      ),
    );
  }
}

class SignMessageScreen extends StatefulWidget {
  @override
  _SignMessageScreenState createState() => _SignMessageScreenState();
}

class _SignMessageScreenState extends State<SignMessageScreen> {
  final TextEditingController _privateKeyController = TextEditingController();
  final TextEditingController _messageController = TextEditingController();
  String? _signature;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          TextField(
            controller: _privateKeyController,
            decoration: InputDecoration(labelText: 'Private Key'),
          ),
          SizedBox(height: 16),
          TextField(
            controller: _messageController,
            decoration: InputDecoration(labelText: 'Message'),
          ),
          SizedBox(height: 16),
          ElevatedButton(
            onPressed: () async {
              setState(() {
                _signature = null;
              });

              try {
                // 从私钥创建以太坊账户
                final privateKey = EthereumAddress.fromHex(_privateKeyController.text).privateKey;
                final credentials = EthPrivateKey.fromHex(privateKey);
                final signer = credentials.toSigner();

                // 要签名的消息(需要先进行Keccak-256哈希)
                final messageHash = keccak256(utf8.encode(_messageController.text));

                // 使用账户私钥签名消息
                final signature = await signer.signMessage(messageHash);

                // 将签名结果转换为十六进制字符串
                setState(() {
                  _signature = signature.toHex();
                });
              } catch (e) {
                print('Error signing message: $e');
              }
            },
            child: Text('Sign Message'),
          ),
          SizedBox(height: 16),
          if (_signature != null)
            Text(
              'Signature: $_signature',
              style: TextStyle(fontSize: 16),
            ),
        ],
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,它允许用户输入一个私钥和一个消息。应用将使用私钥对消息进行签名,并显示签名结果。

关键点解释:

  1. 私钥和消息输入:用户通过TextField输入私钥和消息。
  2. 创建以太坊账户:使用EthereumAddress.fromHex从私钥字符串创建一个以太坊地址对象,然后从中提取私钥。
  3. 创建签名者:使用EthPrivateKey.fromHex从私钥创建一个EthPrivateKey对象,然后调用toSigner方法创建一个签名者对象。
  4. 消息哈希:使用keccak256函数对消息进行哈希处理。注意,以太坊中的消息签名通常是对消息的Keccak-256哈希进行签名。
  5. 签名消息:调用签名者的signMessage方法对消息哈希进行签名。
  6. 显示签名结果:将签名结果转换为十六进制字符串并显示。

请确保在实际应用中妥善管理私钥,避免私钥泄露带来的安全风险。

回到顶部