2017年,比特币协议进行了升级。当使用该升级时,它可以防止交易标识符(txids)在未经付款用户(或在需要多个签名时的签名者群体)的同意下被更改。该升级被称为隔离见证(简称segwit),还为区块中的交易数据提供了额外的容量以及其他几项好处。然而,想要直接获得segwit好处的用户必须接受新输出脚本的支付。
正如在“支付到脚本哈希”中提到的那样,P2SH输出类型的一个优点是支付者(比如Alice)不需要知道接收者(比如Bob)使用的脚本的详细信息。隔离见证升级旨在利用这种机制,允许用户立即开始通过使用P2SH地址来访问许多新的好处。但是,要让Bob获得所有的好处,他需要Alice的钱包使用不同类型的脚本支付给他。这需要Alice的钱包进行升级,以支持新的脚本。
起初,比特币开发人员提出了BIP142,它将继续使用base58check,但带有一个新的版本字节,类似于P2SH的升级。但是,让所有钱包升级到具有新base58check版本的新脚本预计将需要几乎与让它们升级到全新地址格式一样多的工作量,因此几位比特币贡献者着手设计最佳可能的地址格式。他们确定了base58check存在的几个问题:
在开发segwit地址格式的开发人员找到了一种新的地址格式,称为bech32(发音为软“ch”,如“besh thirty-two”)。 “bech”代表BCH,是三位于1959年和1960年发现bech32基于的循环码的缩写人物的首字母。 “32”代表bech32字母表中的字符数(类似于base58check中的58):
示例4-3。Bech32错别字检测
&### x20;地址:
&### x20;bc1p9nh05ha8wrljf7ru236awn4t2x0d5ctkkywmv9sclnm4t0av2vgs4k3au7&### x20;
检测到的错误以粗体和删除线显示。使用bech32地址解码器演示生成。
<figure><img src="https://img.learnblockchain.cn/masterbitcoin3/assets/4.9.png" alt=""><figcaption><p>图 4-9. 相同的Bech32地址,一个使用小写字母编码,另一个使用大写字母编码</p></figcaption></figure>
Bech32地址在每个方面都可能取得成功,除了一个问题。关于它们能够检测错误的数学保证只有在你输入到钱包中的地址长度与原始地址的长度相同时才适用。如果在转录过程中添加或删除任何字符,则该保证将不适用,你的钱包可能会将资金发送到错误的地址。然而,即使没有这个保证,人们仍然认为用户添加或删除字符几乎不可能产生带有有效校验和的字符串,从而确保用户的资金安全。
不幸的是,Bech32算法中一个常量的选择恰巧使得在以字母“p”结尾的地址的倒数第二个位置添加或删除字母“q”变得非常容易。在这种情况下,你还可以多次添加或删除字母“q”。这样做有时会被校验和检测到,但远不如Bech32对字符替换错误的预期频率那样少见。请参见示例4-4。
示例4-4. 扩展Bech32地址的长度而不使其校验和失效
预期的 Bech32 地址:
bc1pqqqsq9txsqp
具有有效校验和的不正确地址:
bc1pqqqsq9txsqqqqp&## x20;
bc1pqqqsq9txsqqqqqqp&## x20;
bc1pqqqsq9txsqqqqqqqqp&## x20;
bc1pqqqsq9txsqqqqqqqqqp&## x20;
bc1pqqqsq9txsqqqqqqqqqqqp
对于初始版本的隔离见证(版本0),这并不是一个实际的问题。对于v0隔离见证输出,只定义了两个有效的长度:22字节和34字节。这对应于bech32地址的长度为42个字符或62个字符,因此某人需要在bech32地址的倒数第二个位置添加或删除字母“q”20次,才能向无效地址发送资金,而钱包无法检测到。然而,如果未来实施了基于隔离见证的升级,这将成为用户的一个问题。
尽管Bech32对于隔离见证v0表现良好,但开发人员不希望在隔离见证的后续版本中不必要地限制输出大小。在没有限制的情况下,在Bech32地址中添加或删除一个“q”可能导致用户意外将资金发送到一个不可花费的输出,或者是可以由任何人花费的输出(允许任何人夺取比特币)。开发人员对Bech32问题进行了彻底的分析,并发现改变算法中的一个常数将消除这个问题,确保对最多五个字符的任何插入或删除的检测失败的频率低于十亿分之一。
具有单个不同常数的Bech32版本被称为修改过的Bech32(Bech32m)。对于相同底层数据,Bech32和Bech32m地址中的所有字符都将相同,除了最后六个字符(校验和)。这意味着钱包需要知道正在使用哪个版本才能验证校验和,但是两种地址类型都包含一个内部版本字节,使得确定这一点变得容易。
为了同时处理Bech32和Bech32m,我们将查看Bech32m比特币地址的编码和解析规则,因为它们包括了解析Bech32地址的能力,并且是比特币钱包的当前推荐地址格式。
Bech32m地址以人类可读部分(HRP)开头。BIP173中有关于创建自己的HRP的规则,但是对于比特币,您只需要了解已选择的HRP,如表4-2所示。
表 4-2. 比特币中Bech32 人类可读部分前缀
| 人类可读部分前缀 | 网络类型 |
|---|---|
| bc | 比特币主网 |
| tb | 比特币测试网 |
HRP 之后是一个分隔符,即数字 "1"。早期的协议分隔符使用了冒号,但某些操作系统和应用程序允许用户双击单词以突出显示以进行复制和粘贴,这样的功能将不会延伸到并且会经过冒号。数字确保双击高亮功能可以与支持 bech32m 字符串的任何程序一起工作(其中包括其他数字)。选择数字 "1" 是因为 bech32 字符串在其他情况下不使用它,以防止数字 "1" 与小写字母 "l" 之间的意外音译。
&### x20;Bech32m 地址的另一部分称为 "数据部分"。这部分包括三个元素:
见证版本
一个单字节,在bech32m比特币地址中作为分隔符之后的单个字符编码。该字母表示SegWit版本。字母“q”是SegWit v0的编码,其中引入了bech32地址的初始版本。字母“p”是SegWit v1的编码(也称为Taproot),在那里开始使用bech32m。SegWit有17个可能的版本,对于比特币来说,bech32m数据部分的第一个字节必须解码为数字0到16(包括0和16)。
见证程序
长度为2到40个字节。对于SegWit v0,见证程序必须是20或32个字节;其他长度都无效。截至目前,对于SegWit v1,唯一定义的长度是32个字节,但以后可能会定义其他长度。
校验和
为6个字符。这是使用 BCH 码创建的,一种错误纠正码(尽管对于比特币地址,稍后我们将看到它仅用于错误检测,而不是纠正)。
\ 让我们通过一个示例来说明这些规则,创建bech32和bech32m地址。在以下所有示例中,我们将使用Python的bech32m参考代码。&### x20;
我们将首先生成四个输出脚本,每个脚本对应于出版时使用的不同segwit输出类型,以及一个对应于尚未定义含义的未来segwit版本。这些脚本列在表4-3中。
表4-3 不同类型segwit输出的脚本
<table><thead><tr><th width="119">输出类型</th><th>示例脚本</th></tr></thead><tbody><tr><td>P2WPKH</td><td>OP_0 2b626ed108ad00a944bb2922a309844611d25468</td></tr><tr><td>P2WSH</td><td>OP_0 648a32e50b6fb7c5233b228f60a6a2ca4158400268844c4bc295ed5e8c3d626f</td></tr><tr><td>P2TR</td><td>OP_1 2ceefa5fa770ff24f87c5475d76eab519eda6176b11dbe1618fcf755bfac5311</td></tr><tr><td>Future Example</td><td>OP_16 0000</td></tr></tbody></table>
\ 对于P2WPKH输出,见证程序包含一个承诺,其构造方式与“P2PKH的传统地址”(见第63页)中的P2PKH输出的承诺完全相同。将公钥传递到SHA256哈希函数中。然后,将结果的32字节摘要传递到RIPEMD-160哈希函数中。该函数的摘要(承诺)放置在见证程序中。
对于付款给见证脚本哈希(P2WSH)输出,我们不使用P2SH算法。而是将脚本传递到SHA256哈希函数中,并使用该函数的32字节摘要作为见证程序。对于P2SH,SHA256摘要再次与RIPEMD-160哈希,但在某些情况下可能不安全;有关详细信息,请参见“P2SH碰撞攻击”(第73页)。使用SHA256而不是RIPEMD-160的结果是P2WSH承诺为32字节(256位),而不是20字节(160位)。
对于付款到taproot(P2TR)输出,见证程序是secp256k1曲线上的一个点。它可以是一个简单的公钥,但在大多数情况下,它应该是一个公钥,该公钥承诺了一些附加数据。我们将在“Taproot”(第178页)中详细了解这个承诺。
对于未来segwit版本的示例,我们简单地使用最高可能的segwit版本号(16)和允许的最小见证程序(2字节)以及空值。
现在我们知道了版本号和见证程序,我们可以将它们转换为bech32地址。让我们使用Python的bech32m参考库快速生成这些地址,然后深入了解发生了什么:
$ github="https://raw.githubusercontent.com"&### x20;
$ wget $github/sipa/bech32/master/ref/python/segwit_addr.py&### x20;
$ python
>>> from segwit_addr import *
>>> from binascii import unhexlify
>>> help(encode)
encode(hrp, witver, witprog)
Encode a segwit address.
>>> encode('bc', 0, unhexlify('2b626ed108ad00a944bb2922a309844611d25468'))
'bc1q9d3xa5gg45q2j39m9y32xzvygcgay4rgc6aaee'
>>> encode('bc', 0,unhexlify('648a32e50b6fb7c5233b228f60a6a2ca4158400268844c4bc295ed5e8c3d626f'))
'bc1qvj9r9egtd7mu2gemy28kpf4zefq4ssqzdzzycj7zjhk4arpavfhsct5a3p'
>>> encode('bc', 1,
unhexlify('2ceefa5fa770ff24f87c5475d76eab519eda6176b11dbe1618fcf755bfac5311'))
'bc1p9nh05ha8wrljf7ru236awm4t2x0d5ctkkywmu9sclnm4t0av2vgs4k3au7'
>>> encode('bc', 16, unhexlify('0000'))
'bc1sqqqqkfw08p'
如果我们打开文件segwit_addr.py并查看代码的操作,我们会注意到bech32(用于segwit v0)和bech32m(用于后续segwit版本)之间唯一的区别是常数:
BECH32_CONSTANT = 1&### x20;
BECH32M_CONSTANT = 0x2bc830a3
接下来我们注意到生成校验和的代码。在校验和的最后一步中,适当的常数通过异或运算合并到值中。这个单一的值是bech32和bech32m之间唯一的区别。
&### x20;校验和创建完成后,数据部分(包括见证版本、见证程序和校验和)中的每个5位字符都转换为字母数字字符。&### x20;
要解码回输出脚本,我们反向工作。首先让我们使用参考库来解码我们的两个地址:
>>> help(decode)
decode(hrp, addr)
Decode a segwit address.
>>> _ = decode("bc", "bc1q9d3xa5gg45q2j39m9y32xzvygcgay4rgc6aaee")
>>> _[0], bytes(_[1]).hex()
(0, '2b626ed108ad00a944bb2922a309844611d25468')
>>> _ = decode("bc",
"bc1p9nh05ha8wrljf7ru236awm4t2x0d5ctkkywmu9sclnm4t0av2vgs4k3au7")
>>> _[0], bytes(_[1]).hex()
(1, '2ceefa5fa770ff24f87c5475d76eab519eda6176b11dbe1618fcf755bfac5311')
我们获得了见证版本和见证程序。 这些可以插入到我们输出脚本的模板中:
\<version> \<program>
例如:
OP_0 2b626ed108ad00a944bb2922a309844611d25468&### x20;
OP_1 2ceefa5fa770ff24f87c5475d76eab519eda6176b11dbe1618fcf755bfac5311
特别注意: 这里需要注意的一个可能错误是,见证版本0对应的是OP_0,使用的是字节0x00,但见证版本1对应的是OP_1,使用的是字节0x51。而见证版本2到16分别对应0x52到0x60。
当实现bech32m编码或解码时,我们强烈建议您使用BIP350提供的测试向量。我们也要求您确保您的代码通过与支付尚未定义的未来segwit版本相关的测试向量。即使您无法在新的比特币功能推出时立即添加支持,这也将帮助您的软件在未来多年保持可用。
私钥可以用多种不同格式表示,所有这些格式都对应于相同的256位数。表4-4显示了用于表示私钥的几种常见格式。不同的格式在不同的情况下使用。十六进制和原始二进制格式在软件内部使用,很少显示给用户。WIF用于钱包之间的密钥导入/导出,并经常用于私钥的QR码(条形码)表示。
私钥格式的现代相关性
早期的比特币钱包软件在初始化新用户钱包时生成一个或多个独立的私钥。当初始密钥集已全部使用完毕时,钱包可能会生成额外的私钥。单个私钥可以导出或导入。每当生成新的私钥或导入私钥时,都需要创建钱包的新备份。
后来的比特币钱包开始使用确定性钱包,其中所有私钥都是从单个种子值生成的。这些钱包只需要在典型的链上使用中备份一次。但是,如果用户从其中一个这些钱包中导出一个单独的私钥,而攻击者获得了该密钥以及钱包的一些非私密数据,他们可能会推导出钱包中的任何私钥,从而允许攻击者窃取所有的钱包资金。此外,无法将密钥导入确定性钱包。这意味着几乎没有现代钱包支持导出或导入单个密钥的功能。本节中的信息主要是对需要与早期比特币钱包兼容的人感兴趣。
有关更多信息,请参阅“分层确定性(HD)密钥生成(BIP32)”。
表 4-4. 不同类型私钥(编码格式)
| 类型 | 前缀 | 描述 |
|---|---|---|
| Hex | None | 64位十六进制字符 |
| WIF | 5 | Base58check 编码: 具有版本前缀 128 和 32 位校验和的 base58。 |
| WIF-compressed | K 或 L | 与上述相同,但在编码之前添加了后缀 0x01。 |
表4-5 展示了以几种不同格式生成的私钥。&### x20;
表4-5。示例:相同的密钥,不同的格式
<table><thead><tr><th width="176">格式</th><th>私钥</th></tr></thead><tbody><tr><td>Hex</td><td>1e99423a4ed27608a15a2616a2b0e9e52ced330ac530edcc32c8ffc6a526aedd</td></tr><tr><td>WIF</td><td>5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn</td></tr><tr><td>WIF-compressed</td><td>KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ</td></tr></tbody></table>
所有这些表示法都是展示相同数字、相同私钥的不同方式。它们看起来不同,但任何一个格式都可以轻松转换为任何其他格式。
常用的术语“压缩私钥”是一个错误的说法,因为当一个私钥被导出为WIF-压缩时,它实际上比“未压缩”私钥长一个字节。这是因为私钥有一个额外的字节后缀(在表4-6中以十六进制表示为01),表示该私钥来自一个较新的钱包,应该只用于生成压缩的公钥。私钥本身并没有被压缩,也不能被压缩。术语“压缩私钥”实际上意味着“只应该从中派生压缩的公钥的私钥”,而“未压缩私钥”实际上意味着“只应该从中派生未压缩的公钥的私钥”。为了避免进一步混淆,您应该只将导出格式称为“WIF-压缩”或“WIF”,而不是将私钥本身称为“压缩”。&### x20;
表4-6显示了相同的密钥,以WIF和WIF-压缩格式编码
表 4-6. 示例:相同的私钥,不同格式
<table><thead><tr><th width="208">格式</th><th>私钥</th></tr></thead><tbody><tr><td>Hex</td><td>1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD</td></tr><tr><td>WIF</td><td>5J3mBbAH58CpQ3Y5RNJpUKPE62SQ5tfcvU2JpbnkeyhfsYB1Jcn</td></tr><tr><td>Hex-compressed</td><td>1E99423A4ED27608A15A2616A2B0E9E52CED330AC530EDCC32C8FFC6A526AEDD01</td></tr><tr><td>WIF-compressed</td><td>KxFC1jmwwCoACiCAWZ3eXa96mBM6tb3TYzGmf6YwgdGWZgawvrtJ</td></tr></tbody></table>
请注意,十六进制压缩私钥格式在末尾多了一个额外的字节(十六进制中的01)。虽然基于Base58编码的版本前缀对于WIF和WIF-compressed格式是相同的(0x80),但数字末尾添加了一个字节会导致Base58编码的第一个字符从5变为K或L。可以将这视为十进制编码中数字100和数字99之间的差异。虽然100比99多一位数字,但它的前缀也是1而不是9。随着长度的变化,前缀也会受到影响。在Base58中,数字前缀5会随着数字长度增加一个字节而变为K或L。
请记住,这些格式不能互换使用。在实现了压缩公钥的新钱包中,私钥只能导出为WIF-compressed格式(带有K或L前缀)。如果钱包是旧版本,并且不使用压缩公钥,则私钥只能导出为WIF格式(带有5前缀)。这里的目标是向导入这些私钥的钱包传达一个信号,告诉它是否必须在区块链中搜索压缩或非压缩的公钥和地址。
如果一个比特币钱包能够实现压缩公钥,它将在所有交易中使用它们。钱包中的私钥将用于从曲线派生出压缩的公钥点。压缩的公钥将用于生成比特币地址,并在交易中使用。从实现了压缩公钥的新钱包中导出私钥时,WIF将被修改,私钥将添加一个字节的后缀01。结果生成的Base58check编码的私钥称为“压缩WIF”,并以K或L字母开头,而不是以5开头,这是来自旧钱包的WIF编码(非压缩)私钥的情况。
\
在接下来的章节中,我们将探讨更高级的密钥和地址形式,比如虚拟地址和纸钱包。
\ 自定义地址是有效的比特币地址,其中包含可读的人类可读信息。例如,1LoveBPzzD72PUXLzCkYAtGFYmK5vYNR33是一个有效的地址,其中的前四个base58字母形成了单词“Love”。自定义地址需要生成和测试数十亿个候选私钥,直到找到一个具有所需模式的比特币地址。虽然自定义地址生成算法中有一些优化,但该过程基本上涉及随机选择一个私钥,派生出公钥,派生出比特币地址,并检查它是否与所需的自定义模式匹配,重复数十亿次直到找到匹配项。
一旦找到与所需模式匹配的自定义地址,就可以使用派生自该地址的私钥来花费比特币,方式与任何其他地址完全相同。自定义地址与任何其他地址一样安全。它们依赖于与任何其他地址相同的椭圆曲线密码学(ECC)和安全哈希算法(SHA)。您无法更轻松地找到具有自定义模式起始的地址的私钥,就像您无法更轻松地找到任何其他地址的私钥一样。
Eugenia是一位在菲律宾从事儿童慈善工作的主任。假设Eugenia正在组织一场筹款活动,并希望使用一个自定义的比特币地址来宣传筹款活动。Eugenia将创建一个以“1Kids”开头的自定义地址,以促进儿童慈善筹款活动。让我们看看这个自定义地址将如何创建,以及对Eugenia的慈善机构安全性意味着什么。
重要的是要意识到,比特币地址只是用base58字母表中的符号表示的数字。像“1Kids”这样的模式搜索可以看作是在范围从1Kids11111111111111111111111111111到1Kidszzzzzzzzzzzzzzzzzzzzzzzzzzzzz之间搜索地址。在这个范围内有大约5829个(大约1.4 × 1051)地址,所有地址都以“1Kids”开头。
表4-7显示了以“1Kids”开头的自定义地址范围。
From 1Kids11111111111111111111111111111&### x20;
&### x20; 1Kids11111111111111111111111111112&### x20;
&### x20; 1Kids11111111111111111111111111113&### x20;
&### x20; …
&### x20;To 1Kidszzzzzzzzzzzzzzzzzzzzzzzzzzzzz
让我们将模式“1Kids”视为一个数字,并看看在比特币地址中可能找到这个模式的频率(见表4-8)。一台普通的桌面电脑,没有任何专门的硬件,可以每秒搜索大约100,000个密钥。
表4-8显示了一个自定义模式(1KidsCharity)的频率和在桌面PC上的平均搜索时间。
| 长度 | 模式 | 频率 | 平均搜索时间 |
|---|---|---|---|
| 1 | 1K | 58次 | < 1毫秒 |
| 2 | 1Ki | 3364次 | 50 毫秒 |
| 3 | 1Kid | 195,000 次 | < 2 秒 |
| 4 | 1Kids | 1100万次 | 1分钟 |
| 5 | 1KidsC | 6.56亿 | 1小时 |
| 6 | 1KidsCh | 380亿 | 2天 |
| 7 | 1KidsCha | 2.2兆(一兆=10^12) | 3-4个月 |
| 8 | 1KidsChar | 128兆 | 13-18年 |
| 9 | 1KidsChari | 7千兆 | 800年 |
| 10 | 1KidsCharit | 400千兆 | 46000年 |
| 11 | 1KidsCharity | 23京(一京=10^16) | 250万年 |
如您所见,即使是有几千台计算机,Eugenia 也不可能很快就创建出 "1KidsCharity" 这个自定义地址。每增加一个字符,搜索的难度就增加了58倍。通常情况下,具有超过七个字符的模式通常是由专用硬件完成的,例如具有多个图形处理单元(GPU)的定制台式机。在 GPU 系统上进行的自定义搜索比在通用 CPU 上快得多。&### x20;
另一种找到自定义地址的方法是将工作外包给一组自定义矿工。自定义矿工池是一种服务,允许那些拥有快速硬件的人通过为他人搜索自定义地址而赚取比特币。以一定费用,Eugenia 可以将对七个字符模式自定义地址的搜索外包,并在几小时内获得结果,而不必花费数月时间在 CPU 上进行搜索。&### x20;
生成自定义地址是一种蛮力练习:尝试一个随机密钥,检查生成的地址是否与所需模式匹配,重复直至成功。
自定义地址在比特币早期很受欢迎,但截至2023年几乎完全从使用中消失。这种趋势有两个可能的原因:
确定性钱包
正如我们在“恢复码”中看到的那样,大多数现代钱包可以通过简单地写下几个单词或字符来备份每个密钥。这是通过使用确定性算法从这些单词或字符派生钱包中的每个密钥来实现的。除非用户为创建的每个自定义地址备份了额外的数据,否则无法在确定性钱包中使用自定义地址。更实际的是,大多数使用确定性密钥生成的钱包根本不允许从自定义地址生成器导入私钥或密钥扭曲。
避免地址重用
使用自定义地址接收多笔支付到同一个地址会在所有这些支付之间创建一个联系。如果Eugenia的非营利组织需要向税务机构报告其收入和支出,那么这可能是可以接受的。然而,这也会降低那些支付给Eugenia或从她那里接收支付的人的隐私。例如,Alice可能想匿名捐赠,Bob可能不希望他的其他客户知道他给Eugenia提供折扣价格。&### x20;
我们不指望在未来会看到很多自定义地址,除非上述问题得到解决。
纸钱包是印在纸上的私钥。通常,纸钱包还包括相应的比特币地址以便使用,但这并非必要,因为地址可以从私钥派生出来。
特别注意: 纸钱包是一种已经过时的技术,对大多数用户来说是危险的。生成它们涉及许多微妙的陷阱,其中最重要的是生成代码可能被“后门”入侵。很多比特币就是这样被盗走的。这里仅仅展示纸钱包供信息参考,不应用于存储比特币。使用恢复码备份您的密钥,可能还可以使用硬件签名设备来存储密钥和签署交易。请勿使用纸钱包。
纸钱包有许多不同的设计和尺寸,具有许多不同的功能。图4-10显示了一个样品纸钱包。
<figure><img src="https://img.learnblockchain.cn/masterbitcoin3/assets/4.10.png" alt=""><figcaption><p>图 4-10. 简单纸钱包的示例</p></figcaption></figure>
\ 有些纸钱包设计成礼物赠送,具有季节性主题,如圣诞节和新年。其他设计用于存放在银行保险箱或保险柜中,私钥以某种方式隐藏,可以使用不透明的刮开贴纸或折叠并用防篡改的粘贴铝箔密封。其他设计则包含私钥和地址的额外副本,形式类似于可拆卸的车票副本,允许您存储多个副本以防火灾、水灾或其他自然灾害。
从比特币最初的公钥设计到现代地址和脚本,如bech32m和支付到taproot,甚至是未来比特币升级的地址,您已经了解到比特币协议如何允许支付者识别应该接收他们支付的钱包。但当实际上是您的钱包接收支付时,您肯定希望确保即使发生了钱包数据丢失,您仍然能够访问这笔资金。在下一章中,我们将看看比特币钱包是如何设计来保护其资金免受各种威胁的。