面向开发者、技术爱好者和初学者的IOTA开发要点

这是专为希望快速熟悉IOTA的开发者和技术爱好者而设计的的IOTA协议自学笔记。技术相关信息均附带了交互式代码片段,可以帮助您快速切入平台并准备好基于此平台构建自己的解决方案。

本指南的主要目标是提供比例均衡的“深潜式”技术资料和要点。如果您事先并不熟悉IOTA,那么强烈建议按照特定的顺序进行学习,以正确的理解所有重要的IOTA概念。

这篇教程是由Github Repo自动生成的。访问repo协议以获取更多信息。

IOTA 101:要点和IOTA术语

IOTA是一种开放源代码的分布式账本技术(DLT),具有以下特点:无需许可,无费用和去信任。与区块链技术不同,IOTA交易以有向无环图(DAG)的形式存储,称为Tangle。

面向开发者、技术爱好者和初学者的IOTA开发要点

交易

缠结中的交易(TXs)可以有两种类型:

  • 价值交易(IOTA token代表价值交易中的资金)
  • 零价值交易(meta transactions,元交易)

每笔交易都附加到一个特定的IOTA地址。IOTA协议没有采用矿工机制来验证交易。共识是由网络参与者自己达成的。想要广播新交易的每个参与者都必须验证2个过去的交易,并将交易附加为一个子节点。没有子交易(等待确认)的交易被称为tips。

种子,私钥和地址

一个唯一的IOTA地址和其相应的私钥是由种子确定生成的。你可以基于一个种子生成(253 - 1)个不同的地址/私钥。任何人都可以发送任何东西到给定的地址,但只有种子所有者可以从IOTA地址发送资金,因为只有种子所有者能够生成相应的私钥。

IOTA协议使用一次性签名(OTS),确切的说是温特尼茨一次性签名 (Winternitz OTS,简称WOTS),来“验证”私钥是否与给定的地址相匹配。所以私钥(特别是私钥的密钥片段)是使用WOTS签署交易时的关键组件。

这就是为什么我们不应该使用已经支出过资金的IOTA地址,而是应该生成并使用新的IOTA地址的原因。请注意,上述情况只适用于有过支出的地址,如果一个地址只用来接受资金/数据是完全可以重复使用的,用多少次也没有关系。

总结一下重点:只有种子需要记录并保存起来。其他所有东西(私钥和地址)都可以通过种子安全地生成。

IOTA节点

Tangle运行在一个名为mainnet的IOTA节点的分布式网络上。还有其他单独的用于特定目的“Tangles”:例如Devnet,SPAMnet等。您可以使用公共API调用集合,通过公共节点与网络进行交互。您可以与给定网络的任何公共节点进行交互,因为所有节点都在整个网络中“gossiping”所有的TXs。截至目前,节点是由称为IRI(IOTA参考实现)的参考软件驱动的。

还有一点很重要,就是您不应与节点交换任何敏感信息(比如Seed)。每个敏感操作(如TX签名)都在客户端进行,节点只是一个将您的TX传送到IOTA网络的“信使”。 面向开发者、技术爱好者和初学者的IOTA开发要点

自定义节点

您甚至可以运行自己的IOTA节点并积极参与整个网络,自营节点对开发者有很大的帮助。IRI的实际实现通过称为ZeroMQ的消息传递平台提供有用的实时信息,因此开发者可以利用它。

API调用

有面向主要编程语言的现成库,因此您可以通过更多“开发者友好”的方式来与IOTA网络交互。无论如何,您也可以不借助任何库,只使用纯粹的API调用来与IOTA网络进行交互。这取决于你自己。

三进制

在讨论IOTA时,很难避免提到三进制数字系统。IOTA基于三进制系统,因此必须了解这方面的一些基本术语。

三进制

三进制是以3为基数的进制,它与基数为2的二元数不同。二进制有两个状态(0和1),三进制则识别三个状态(例如-1,0,1)。

Trit

一个Trite类似于一个bit。它是最小的数字,有三个状态。 Trit = Trinary digit

Tryte

Tryte由三个trit组成,意味着有27种组合。

3³=27

很快你就会发现,几乎所有的东西都被编码在IOTA世界的Trytes中。Tryte在视觉上用[A..Z]字母和数字9表示。一个字符表示一个tryte(这里又出现了27种可能的字符)。

小例子:

import iota #importing PyOTA library to interact with
from pprint import pprint

TrytesAsBytes = b"YZJEATEQ9JKLZ" # some data encoded in Trytes (byte string in Python, not unicode string)
Trytes = iota.TryteString(TrytesAsBytes) # initializing TryteString type from the PyOTA library - great help while dealing with Trytes/Trits, etc.
pprint(Trytes) # getting the same data however using TryteString type of PyOTA library
pprint("Number of Trytes: %s" % len(TrytesAsBytes))
Trits = Trytes.as_trits() # converting Trytes to Trits
pprint(Trits,compact=True)
pprint("Number of trits: %s" % len(Trits)) # Number of trits is three times the number of trytes obviously

结果:

TryteString(b'YZJEATEQ9JKLZ')
'Number of Trytes: 13'
[1, -1, 0, -1, 0, 0, 1, 0, 1, -1, -1, 1, 1, 0, 0, -1, 1, -1, -1, -1, 1, -1, 0,
-1, 0, 0, 0, 1, 0, 1, -1, 1, 1, 0, 1, 1, -1, 0, 0]
'Number of trits: 39'

IOTA 101:基本网络互动

本教程基于一个名为PyOTA的官方Python库。它封装了所有官方的IRI API调用,并且可以通过PIP在任何Python环境中安装。如果您在Azure笔记本上查看本教程,则不必担心,它已经为您配置了Python环境,包括PyOTA。因此,您可以直接在Web浏览器中使用代码片段。

无论如何,本教程中的大多数信息都与编程语言无关,因此只是在不同编程语言的实现细节上会略有不同。无论您要用于项目的编程语言是什么,顶级细节都是相同的。我基于Python语言编写了所有的代码片段,因为即使对于初学者来说它也是相对比较容易理解的编程语言。

连接到IOTA节点

在与IOTA网络进行交互之前,您需要一个正在运行的IOTA节点的URL。有十多个提供公共节点目录的网站,可使用搜索引擎搜索到它们。

为了学习本教程,我使用了一个名为Carriota Field的项目。它是一个负载均衡器和激励器,可以让您轻松的访问参与者节点池(有数千个节点)。

优点:不必关心公共节点以及节点是否正在运行。

缺点:事先不知道会是哪个特定节点将处理您的API调用。因此,在某些方面,API调用的结果可能会有所不同,因为一些API调用依赖于特定的节点,比如GetNodeInfo()。

节点未同步

即使节点已经启动并运行,它也可能没有完全准备好正确地处理API调用。节点应该是“同步的”,意思是节点必须知道Tangle中的所有TXs。最好避免使用未完全同步的节点。

健康检查

连接到IOTA节点后,就应该能发送API调用了。但是,最好事先进行一些检查,以确定给定的节点是否处于良好状态。因为任何人都可以运行IOTA节点,节点状态有好有坏,因此先对节点进行一次健康检查是一个很好的实践。

基本的健康检查可以通过GetNodeInfo() API调用来完成。它返回与给定的IOTA节点有关的基本信息。

import iota #importing PyOTA library to interact with
from pprint import pprint

NodeURL = "https://field.carriota.com:443"

api=iota.Iota(NodeURL) # ctor initialization of the PyOTA library
result = api.get_node_info() # basic API call to double check health conditions
pprint(result) # result is printed out

# Basic check whether node is in sync or not
# Elementary rule is that "latestMilestoneIndex" should equal to "latestSolidSubtangleMilestoneIndex" or be very close
if abs(result['latestMilestoneIndex'] - result['latestSolidSubtangleMilestoneIndex']) > 3 :
print ("\r\nNode is probably not synced!")
else:
print ("\r\nNode is probably synced!")

结果:

{'appName': 'IRI',
'appVersion': '1.4.2.4',
'duration': 25,
'fieldName': 'TOBG',
'fieldPublicId': '91edc0d6b0911401',
'fieldVersion': '0.1.6',
'jreAvailableProcessors': 16,
'jreFreeMemory': 161380952,
'jreMaxMemory': 9320792064,
'jreTotalMemory': 1841299456,
'jreVersion': '1.8.0_171',
'latestMilestone': TransactionHash(b'GAR9SHMASXJIVUVNJKZWYYWRJBXCNMQHT9MHGGFM9FKZFTDKLSYQXVGAKJX9XQZSSWFCLZUSGXIPA9999'),
'latestMilestoneIndex': 549411,
'latestSolidSubtangleMilestone': TransactionHash(b'GAR9SHMASXJIVUVNJKZWYYWRJBXCNMQHT9MHGGFM9FKZFTDKLSYQXVGAKJX9XQZSSWFCLZUSGXIPA9999'),
'latestSolidSubtangleMilestoneIndex': 549411,
'neighbors': 12,
'packetsQueueSize': 0,
'time': 1529573518899,
'tips': 9994,
'transactionsToRequest': 51}

Node is probably synced!

请注意:当使用Carriota Field服务时,这种类型的检查是没有用的,因为您的其他API调用可能是由不同的节点提供的,而这些节点显然没有被检查过。另一方面,Carriota Field使用了一些智能技术,这些技术可能会阻止您使用状况不佳的节点。

生成IOTA种子和IOTA地址

由于IOTA网络是无许可的网络类型,任何人都可以使用它并与之交互。在任何阶段都不需要中央集权管理。

所以任何人都可以随时生成自己的种子和各自的私钥/地址。强烈建议不要使用在线生成器。

种子是给定地址的唯一密钥。任何拥有种子的人也拥有与各自IOTA地址相关的所有资金。

种子和地址仅由字符[A-Z]和数字9组成,长度是固定的81个字符。在IOTA地址中通常还会使用另外9个字符(因此其总长度是90),这是一个校验和。它提供了一种防止在处理IOTA地址时出现错误操作的方法。

种子

种子生成是一个基于对种子中每个字符进行随机查询的过程。当然,你应该始终使用加密安全的伪随机生成器!

import random
chars=u'9ABCDEFGHIJKLMNOPQRSTUVWXYZ' #27 characters - max number you can express by one Tryte - do you remember?
rndgenerator = random.SystemRandom() #cryptographically secure pseudo-random generator

NewSeed = u''.join(rndgenerator.choice(chars) for _ in range(81)) #generating 81-chars long seed. This is Python 3.6+ compatible
print(NewSeed)
print("Length: %s" % len(NewSeed))

结果:

PYDHTCNKMTVRGORKCKDCHPMIRAIVGB9FZFGUJWZVAQYWUVOEOJUVDEMNGOLWWSOG9QLUHDNVEFYOBFEIK
Length: 81

另外,您也可以利用PyOTA库,因为它自己实现了伪随机种子生成器(兼容Python 2.7+)。这两种方法之间的差异非常微妙。两者的明显区别是:一个是完全独立的,一个是依赖IOTA库的。

from iota.crypto.types import Seed #importing PyOTA library to interact with

NewSeed = Seed.random()
print(NewSeed)
print("Length: %s" % len(NewSeed))

结果:

SVXJAGAMEMBFFXEWZMLEHYXJNUAQQEEPBLWDZOQB9VPDGPWEQMMMXMDYVTNDDPNUHZXKSUXSTSVYIBCTM

Length: 81

地址

从种子中获得地址有一个确定性的函数。它基本上是一个从索引0, 1, 2的地址开始的地址索引集合。

在生成地址时,您还应该知道有三种不同的所谓安全级别(1,2和3)。每个安全级别生成完全独立的地址池。安全级别1的地址与安全级别2的地址完全不同等等。

简而言之,安全级别表明从种子生成私钥的困难程度,以及它的大小。安全等级越高,给定私钥越安全(越长)。 默认安全级别为2。大多数IOTA钱包使用安全级别 = 2,用户通常没有权限更改它。所以在设计你的应用时要格外小心。

只要种子,地址索引和安全级别相同,您始终可以获得相同的地址和相应的私钥:

PrivateKey/Address=fce(Seed,AddressIndex,SecurityLevel)

普通示例:

PrivateKey/Address1=fce(Seed,1,2)

PrivateKey/Address2=fce(Seed,2,2)

PrivateKey/Address3=fce(Seed,3,2)

面向开发者、技术爱好者和初学者的IOTA开发要点

import iota
from pprint import pprint

# For this particular task the Node URL is not important as it will not be contacted at all
# However it has to be well-formatted URI
NodeURL = "https://field.carriota.com:443"
MySeed = b"WKQDUZTGFKSSLACUCHHLZRKZBHSDSCEBHKUPDLKFBQALEBKDMFRPUQGZRXAADPG9TSRTZGGBZOFRJCFMM"

api=iota.Iota(NodeURL,
seed = MySeed) # if you do not specify a seed, PyOTA library randomly generates one for you under the hood

# Let's generate 3 addresses using default security level=2.
# It is deterministic function - it always generates same addresses as long as the Seed, Security Level and Index are the same
result = api.get_new_addresses(index=0,
count=3,
security_level=2)
pprint(result)

结果:

{'addresses': [Address(b'HRLKBQUZAEB9HIVWJEWVDYQ9G9VRQXQAXR9ZWGBFQJKRPOPJYHGAT9LBEIE9RWRMUFSNLCWYHQGYAECHD'),
Address(b'XEXIDJJTANADOUBPWTCSPPRYYRTITRAHDEOZAEXWDPCYKUPTFMKVQM9KCPPLOCESFRGRVSIYZHXQZNYKC'),
Address(b'KY9DLZCHET9ATLMADPXGDVDYMPHKRKQPJZ9MB9HEIMMFCRRTNJIJIHPKGZNKKDTFMYPZRRQYAQKVAHMYX')]}

毫无疑问,NodeURL对于这个任务并不重要。如前所述,有些行为纯粹是在客户方进行的。这是其中之一。

无论如何,对于PyOTA库,您可以直接使用地址生成器组件,而不是整个库。在这两种情况下,输出都是相等的。

from iota.crypto.addresses import AddressGenerator

MySeed = b"WKQDUZTGFKSSLACUCHHLZRKZBHSDSCEBHKUPDLKFBQALEBKDMFRPUQGZRXAADPG9TSRTZGGBZOFRJCFMM"

#security level is defined during generator init
generator = AddressGenerator(seed=MySeed,
security_level=2)

result = generator.get_addresses(0, 3) #index, count
pprint(result)

结果:

[Address(b'HRLKBQUZAEB9HIVWJEWVDYQ9G9VRQXQAXR9ZWGBFQJKRPOPJYHGAT9LBEIE9RWRMUFSNLCWYHQGYAECHD'),
Address(b'XEXIDJJTANADOUBPWTCSPPRYYRTITRAHDEOZAEXWDPCYKUPTFMKVQM9KCPPLOCESFRGRVSIYZHXQZNYKC'),
Address(b'KY9DLZCHET9ATLMADPXGDVDYMPHKRKQPJZ9MB9HEIMMFCRRTNJIJIHPKGZNKKDTFMYPZRRQYAQKVAHMYX')]

验证IOTA地址

如前所述,IOTA地址由81个trytes([A..Z9]字符)或90个trytes(包括校验和)组成的。当您想确保给出的地址是有效的时(没有拼写错误等),校验和非常有用,因此应该鼓励你的应用的用户使用包括校验和在内的90个trytes的IOTA地址。

PyOTA库可以帮助您处理一些基本的模式,如验证地址,生成校验和等。

import iota
from pprint import pprint

# some IOTA address
Adr = iota.Address(b"CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW")

pprint("Original input excl. checksum address:")
pprint(Adr)
print("Length: %s" % len(Adr))

AdrInclCheckSum = Adr.with_valid_checksum()
print("\nInput address including checksum:")
pprint(AdrInclCheckSum) # the last 9 trytes is the checksum
print("Length incl checksum: %s" % len(AdrInclCheckSum))

结果:

'Original input excl. checksum address:'
Address(b'CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW')
Length: 81

Input address including checksum:
Address(b'CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RWTHBIRSXTA')
Length incl checksum: 90

您应该始终确保您的应用正在处理IOTA地址是有效的。请注意,您还应该确保地址的长度是正确的,并且只包含允许的字符。

例如下面的片段:

手动检查长度 - 因为PyOTA库也接受少于80个trytes的地址。

使用检查有效字符 - PyOTA库将在这种情况下抛出异常。

import iota
from pprint import pprint

InputAddr = b"CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RWTHBIRSXTA"
if len(InputAddr)!=90:
print("Incorrect lenght of the given address. Please, use an address including checksum.")
exit(2)

try:
# address including checksum
Adr2 = iota.Address(InputAddr)
except :
print("Not valid input address given")
exit(1)

pprint("Input address incl checksum:")
pprint(Adr2)
print("Is it valid addr based on checksum? %s" % (Adr2.is_checksum_valid()))

print("\nInput address excl checksum:")
pprint(Adr2[:81]) # return only first 81 characters

结果:

'Input address incl checksum:'
Address(b'CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RWTHBIRSXTA')
Is it valid addr based on checksum? True

Input address excl checksum:
TryteString(b'CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW')

IOTA 101:交易

交易是可以广播到网络的最小数据。每笔交易由固定的2673个trytes组成。交易(TXs)基本上有两种类型:

  • 价值TX:这些交易改变了IOTA token的所有权。两种可能的状态:支出TXs或接收TXs。
  • 非价值TX:这些交易包括可以从Tangle中广播和读取的一段数据。

交易在一个名为Bundle的封包中广播。Bundle可以由多个TXs组成。Bundle由Tangle作为一个单独的实体进行处理,也就是说,只有整个bundle(以及内含的所有TXs)由网络确认。即使您仅广播单个交易,您也必须准备一个bundle。Bundle也被描述为原子的,因为每个bundle都包含了非常有效地处理它所需的所有信息。

每个交易都包含几个字段,让我们列出最重要的字段:

  • Value:值可以是正数 = 接收的IOTA token,负数 = 支出的IOTA token,零 = 仅广播数据。
  • Address:与给定交易关联的IOTA地址。Value可以表示它是接收者地址还是发送者地址。
  • Tag:由用户定义的标记,可用于搜索等。
  • SignatureMessageFragment:这是最长的属性(2187个trytes),可以包括:支出TX的基于私钥的交易签名或者非价值TX的数据片段(message),它也可以留空(如果是非价值TX)。
  • tTrunkTransaction / branchTransaction :这两个字段引用了Tangle中bundle将要批准的一些之前的交易(tips)。
  • Timestamp

面向开发者、技术爱好者和初学者的IOTA开发要点

发送IOTA交易的过程可以总结为5个步骤:

  • 创建具有给定属性的交易:Value ,address以及可能还有tag或message。
  • 生成包含所有交易的bundle。bundle完成后,不能再向其中添加新的交易。它基本上意味着生成一个Bundle hash。
  • 在Tangle中搜索将要验证的两个tips。如何搜索它们并没有严格的规则,但通常建议将搜索放在节点侧实施的tip选择算法上进行。
  • 为bundle中的每个交易执行PoW。PoW的结果是与每个交易一起存储的Nonce和交易散列。
  • 最后,将整个bundle广播到网络。

Bundle的秘密

有一个秘密需要分享一下。你必须理解一件重要的事情,Bundle是一种顶级构造,它将所有相关的交易链接到一个实体中,然而事实上bundle本身并未被广播。您仍然会“只”广播单个交易的集合。IOTA协议将所有交易视为bundle的一部分,但任何数据对等都是基于单个交易(在trytes中)。

换句话说,一个bundle可以在任何时候通过字段bundle hash ,current indexlast index从交易集合中重构。

非价值交易

让我们先从非价值交易开始,因为它们对初学者来说相对简单一点。

请注意,该章分为两个单独的部分。

在第一部分中,示例将说明整个过程并尽可能的描述实现细节。当你想在每个步骤中尝试不同的设置时,它是很有用的。

在第二部分中,将充分的利用PyOTA库,它将所有的实现细节隐藏在幕后进行。不用说,如果你对第一部分的细节不感兴趣,那么你就可以从第二部分开始。

创建和广播交易

创建交易

首先让我们创建交易(离线,在内存中)并设置主要字段。

BTW:Timestamp字段由PyOTA库自动定义,因此请确保在使用其他面向IOTA的库时也定义一下它。创建bundle hash时,这是一个重要的字段。

在本章的示例中,我们将向两个不同的IOTA地址广播元交易(仅包含数据的交易)。

import iota
from datetime import datetime
from pprint import pprint

MySeed = b"HGW9HB9LJPYUGVHNGCPLFKKPNZAIIFHZBDHKSGMQKFMANUBASSMSV9TAJSSMPRZZU9SFZULXKJ9YLAIUA"
TargetAddress1 = b"CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9"
TargetAddress2 = b"CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW"

NowIs = datetime.now() # get a actual date & time - just to have some meaningfull info

# preparing transactions
pt = iota.ProposedTransaction(address = iota.Address(TargetAddress1), # 81 trytes long address
message = iota.TryteString.from_unicode('Here comes a first message. Now is %s' % (NowIs)),
tag = iota.Tag(b'HRIBEK999IOTA999TUTORIAL'), # Up to 27 trytes
value = 0)

pt2 = iota.ProposedTransaction(address = iota.Address(TargetAddress2), # 81 trytes long address
message = iota.TryteString.from_unicode('Here comes a second message. Now is %s' % (NowIs)),
tag = iota.Tag(b'HRIBEK999IOTA999TUTORIAL'), # Up to 27 trytes
value = 0)
# besides the given attributes, library also adds a transaction timestamp

print("Created transaction objects:\n")
pprint(vars(pt))
print("\n")
pprint(vars(pt2))

结果:

Created transaction objects:

{'_legacy_tag': None,
'address': Address(b'CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9'),
'attachment_timestamp': 0,
'attachment_timestamp_lower_bound': 0,
'attachment_timestamp_upper_bound': 0,
'branch_transaction_hash': TransactionHash(b'999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'bundle_hash': None,
'current_index': None,
'hash': None,
'is_confirmed': None,
'last_index': None,
'message': TryteString(b'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA'),
'nonce': Nonce(b'999999999999999999999999999'),
'signature_message_fragment': None,
'tag': Tag(b'HRIBEK999IOTA999TUTORIAL999'),
'timestamp': 1530080759,
'trunk_transaction_hash': TransactionHash(b'999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'value': 0}

{'_legacy_tag': None,
'address': Address(b'CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW'),
'attachment_timestamp': 0,
'attachment_timestamp_lower_bound': 0,
'attachment_timestamp_upper_bound': 0,
'branch_transaction_hash': TransactionHash(b'999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'bundle_hash': None,
'current_index': None,
'hash': None,
'is_confirmed': None,
'last_index': None,
'message': TryteString(b'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA'),
'nonce': Nonce(b'999999999999999999999999999'),
'signature_message_fragment': None,
'tag': Tag(b'HRIBEK999IOTA999TUTORIAL999'),
'timestamp': 1530080759,
'trunk_transaction_hash': TransactionHash(b'999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'value': 0}

完成Bundle

创建所有单个交易后,就可以准备并完成bundle。在准备bundle时,至少需要指定准备好的交易列表。在非价值交易的情况下,这个过程非常简单。但是,价值交易的情况会稍微复杂一点 - 稍后会讲到。

Bundle的最终确定包括以下几个任务:

  • 每个交易都被索引。设置属性current_index / last_index
  • 生成Bundle hash( Sponge函数 +规范化)并分配给每个交易。
  • 在非价值交易的情况下,signatureMessageFragmentmessage字段的副本。
  • PyOTA库还检查message字段中的数据是否大于交易允许的范围(2187个trytes)。如果是这种情况,那么它会处理它并将数据分成几个交易。

请注意,bundle最终确定后,意味着您不能再向bundle中添加新的交易。

简单地说,bundle hash是bundle中所有交易的加密“指纹”。它是交易的唯一表示,因此只要交易是相同的(包括它们的特定顺序),bundle hash也是相同的。

BundleHash=fce(address,value,legacytag,timestamp,currentindex,lastindex)

您可能想知道taglegacy_tag之间有什么区别。Tag包括实际上在交易创建期间定义的标签。Legacy_tag也基于此,但它在bundle hashing确保bundle hash在TX签署时可以安全使用的规范化过程中被修改。这就是bundle hash有时被称为normalized bundle hash的原因 。

Bundle将集合中的第一个交易称为tail_transaction

# preparing bundle that consists of both transactions prepared in the previous example
pb = iota.ProposedBundle(transactions=[pt,pt2]) # list of prepared transactions is needed at least

# generate bundle hash using sponge/absorb function + normalize bundle hash + copy bundle hash into each transaction / bundle is finalized
pb.finalize()

#bundle is finalized, let's print it
print("\nGenerated bundle hash: %s" % (pb.hash))
print("\nTail Transaction in the Bundle is a transaction #%s." % (pb.tail_transaction.current_index))

print("\nList of all transactions in the bundle:\n")
for txn in pb:
pprint(vars(txn))
print("")

结果:

Generated bundle hash: TL9EHBGBIDCTLCARLPKBSUQIG9CKQPDGTLOVNLG9TALAGEWQRBEK9DPLDDBGOAZIREKGYPYFCWJCTOADW

Tail Transaction in the Bundle is a transaction #0.

List of all transactions in the bundle:

{'_legacy_tag': Tag(b'PVIBEK999IOTA999TUTORIAL999'),
'address': Address(b'CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9'),
'attachment_timestamp': 0,
'attachment_timestamp_lower_bound': 0,
'attachment_timestamp_upper_bound': 0,
'branch_transaction_hash': TransactionHash(b'999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'bundle_hash': BundleHash(b'TL9EHBGBIDCTLCARLPKBSUQIG9CKQPDGTLOVNLG9TALAGEWQRBEK9DPLDDBGOAZIREKGYPYFCWJCTOADW'),
'current_index': 0,
'hash': None,
'is_confirmed': None,
'last_index': 1,
'message': TryteString(b'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA'),
'nonce': Nonce(b'999999999999999999999999999'),
'signature_message_fragment': Fragment(b'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'tag': Tag(b'HRIBEK999IOTA999TUTORIAL999'),
'timestamp': 1530080759,
'trunk_transaction_hash': TransactionHash(b'999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'value': 0}

{'_legacy_tag': None,
'address': Address(b'CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW'),
'attachment_timestamp': 0,
'attachment_timestamp_lower_bound': 0,
'attachment_timestamp_upper_bound': 0,
'branch_transaction_hash': TransactionHash(b'999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'bundle_hash': BundleHash(b'TL9EHBGBIDCTLCARLPKBSUQIG9CKQPDGTLOVNLG9TALAGEWQRBEK9DPLDDBGOAZIREKGYPYFCWJCTOADW'),
'current_index': 1,
'hash': None,
'is_confirmed': None,
'last_index': 1,
'message': TryteString(b'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA'),
'nonce': Nonce(b'999999999999999999999999999'),
'signature_message_fragment': Fragment(b'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'tag': Tag(b'HRIBEK999IOTA999TUTORIAL999'),
'timestamp': 1530080759,
'trunk_transaction_hash': TransactionHash(b'999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'value': 0}

在这个阶段,您还可以看到我们最终的bundle是如何进行编码的。这也证明了bundle不会广播本身,只有交易列表才是重要的。

Trytes = pb.as_tryte_strings() # bundle as trytes
pprint(Trytes)

结果:

[TransactionTrytes(b'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW999999999999999999999999999HRIBEK999IOTA999TUTORIAL999TFDCQZD99A99999999A99999999TL9EHBGBIDCTLCARLPKBSUQIG9CKQPDGTLOVNLG9TALAGEWQRBEK9DPLDDBGOAZIREKGYPYFCWJCTOADW999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999HRIBEK999IOTA999TUTORIAL999999999999999999999999999999999999999999999999999999999'),
TransactionTrytes(b'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9999999999999999999999999999PVIBEK999IOTA999TUTORIAL999TFDCQZD99999999999A99999999TL9EHBGBIDCTLCARLPKBSUQIG9CKQPDGTLOVNLG9TALAGEWQRBEK9DPLDDBGOAZIREKGYPYFCWJCTOADW999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999HRIBEK999IOTA999TUTORIAL999999999999999999999999999999999999999999999999999999999')]

选择两个tips

如前所述,为了进行网络参与,您需要找到两个需要与您的bundle一起验证的tips。这将被外包给IOTA节点,因此这会是我们第一次与网络交互(通过API调用get_transactions_to_approve )。

需要一个depth参数。在确定tips时,它指示节点在过去应该经过多少里程碑。价值越高,网络越好,但需要的资源越多。depth = 3被认为是一个均衡的折中方案。

它应该返回两个选定的tips作为branch和trunk交易。这些交易将在广播bundle时用到。

api = iota.Iota("https://field.carriota.com:443") # selecting IOTA node via Field service

gta = api.get_transactions_to_approve(depth=3) # get tips to be approved by your bundle
pprint(gta)

结果:

{'branchTransaction': TransactionHash(b'KQBDDBHWHVLQSJFENGWRBHIOULOMC9GXDW99ECMVFHZXGFZQJOXATSMDLYQJFBZCXA9IJ9SQUXAFZ9999'),
'duration': 1700,
'fieldName': 'F-Node Montabaur',
'fieldPublicId': '1bdeeb759f960554',
'fieldVersion': '0.1.6',
'trunkTransaction': TransactionHash(b'RRYDZLPS9IWCSWRROPQQJTKBRONOJNLUHVHFXFEOZAVGFEOUAHXWWHJSHAGWSSNSMWHN9SEYNBOEA9999')}

执行PoW

PoW是一个相对简单的需要解决的加密难题。它代表您的交易的能源成本。它还有助于最小化网络中某些攻击媒介的风险。

这项任务也可以外包给IOTA节点。为了执行PoW,你需要选择tips,最终bundle(在trytes中)以及Minimum Weight Magnitude参数。这个参数定义了给定的加密拼图应该如何被网络接受。到目前为止,在mainnet中您应该至少设置min_weight_magnitude = 14。

请注意,每次交易都会执行POW,因此可能需要一些时间。这就是为什么不建议bundle中有超过30笔交易的原因。

一旦成功执行PoW,就会返回修改后的bundle(trytes中所有交易的列表)以便广播。

特别信息:

每个交易都计算字段nonce和transaction hash。
Trunk和Branch tips在bundle中的交易间正确地映射。检查第一个TX引用第二个TX的trunk等。

print("Performing POW... Wait please...\n")
att = api.attach_to_tangle(trunk_transaction=gta['trunkTransaction'], # first tip selected
branch_transaction=gta['branchTransaction'], # second tip selected
trytes=Trytes, # our finalized bundle in Trytes
min_weight_magnitude=14) # MWMN
pprint(att)

结果:

Performing POW... Wait please...

{'duration': 12267,
'fieldName': 'iota-fn01.sairai.de',
'fieldPublicId': '45f52c5a2e829a00',
'fieldVersion': '0.1.6',
'trytes': [TransactionTrytes(b'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9999999999999999999999999999PVIBEK999IOTA999TUTORIAL999TFDCQZD99999999999A99999999TL9EHBGBIDCTLCARLPKBSUQIG9CKQPDGTLOVNLG9TALAGEWQRBEK9DPLDDBGOAZIREKGYPYFCWJCTOADWNNNY9QAGVOOGTCIGBBEPIYISUZKNJYUQMROBTZARQVPHONNLQNQFAHTBNXMYWDFHAHMNRJ9URLKX99999RRYDZLPS9IWCSWRROPQQJTKBRONOJNLUHVHFXFEOZAVGFEOUAHXWWHJSHAGWSSNSMWHN9SEYNBOEA9999HRIBEK999IOTA999TUTORIAL999RLISAKGKE999999999L99999999QRZIERPEJFLKTIVKEBQIHJXTAX9'),
TransactionTrytes(b'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW999999999999999999999999999HRIBEK999IOTA999TUTORIAL999TFDCQZD99A99999999A99999999TL9EHBGBIDCTLCARLPKBSUQIG9CKQPDGTLOVNLG9TALAGEWQRBEK9DPLDDBGOAZIREKGYPYFCWJCTOADWRRYDZLPS9IWCSWRROPQQJTKBRONOJNLUHVHFXFEOZAVGFEOUAHXWWHJSHAGWSSNSMWHN9SEYNBOEA9999KQBDDBHWHVLQSJFENGWRBHIOULOMC9GXDW99ECMVFHZXGFZQJOXATSMDLYQJFBZCXA9IJ9SQUXAFZ9999HRIBEK999IOTA999TUTORIAL999GDVSAKGKE999999999L99999999OOLRWTCMJMIQIAUVWBY9TVMZAJJ')]}

您最终还可以预览使用交易对象修改了哪些特定字段。bundle已准备好进行广播,因此它也会显示将存储在网络中的所有字段/值。

# show what has been broadcasted - hash transaction + nonce (POW)
print("Final bundle including POW and branch/trunk transactions:\n")
for t in att['trytes']:
pprint(vars(iota.Transaction.from_tryte_string(t)))
print("")

包含POW和branch/trunk交易的最终bundle:

{'_legacy_tag': Tag(b'PVIBEK999IOTA999TUTORIAL999'),
'address': Address(b'CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9'),
'attachment_timestamp': 1530081729891,
'attachment_timestamp_lower_bound': 0,
'attachment_timestamp_upper_bound': 12,
'branch_transaction_hash': TransactionHash(b'RRYDZLPS9IWCSWRROPQQJTKBRONOJNLUHVHFXFEOZAVGFEOUAHXWWHJSHAGWSSNSMWHN9SEYNBOEA9999'),
'bundle_hash': BundleHash(b'TL9EHBGBIDCTLCARLPKBSUQIG9CKQPDGTLOVNLG9TALAGEWQRBEK9DPLDDBGOAZIREKGYPYFCWJCTOADW'),
'current_index': 0,
'hash': TransactionHash(b'SYDECKQB9BHWFUYOZMANREHOCXHCOPY9JCSZSNIZRNDJTZXHHSESCCLQOENGRVFLDMNXYAHHEWRDZ9999'),
'is_confirmed': None,
'last_index': 1,
'nonce': Nonce(b'QRZIERPEJFLKTIVKEBQIHJXTAX9'),
'signature_message_fragment': Fragment(b'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'tag': Tag(b'HRIBEK999IOTA999TUTORIAL999'),
'timestamp': 1530080759,
'trunk_transaction_hash': TransactionHash(b'NNNY9QAGVOOGTCIGBBEPIYISUZKNJYUQMROBTZARQVPHONNLQNQFAHTBNXMYWDFHAHMNRJ9URLKX99999'),
'value': 0}

{'_legacy_tag': Tag(b'HRIBEK999IOTA999TUTORIAL999'),
'address': Address(b'CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW'),
'attachment_timestamp': 1530081719485,
'attachment_timestamp_lower_bound': 0,
'attachment_timestamp_upper_bound': 12,
'branch_transaction_hash': TransactionHash(b'KQBDDBHWHVLQSJFENGWRBHIOULOMC9GXDW99ECMVFHZXGFZQJOXATSMDLYQJFBZCXA9IJ9SQUXAFZ9999'),
'bundle_hash': BundleHash(b'TL9EHBGBIDCTLCARLPKBSUQIG9CKQPDGTLOVNLG9TALAGEWQRBEK9DPLDDBGOAZIREKGYPYFCWJCTOADW'),
'current_index': 1,
'hash': TransactionHash(b'NNNY9QAGVOOGTCIGBBEPIYISUZKNJYUQMROBTZARQVPHONNLQNQFAHTBNXMYWDFHAHMNRJ9URLKX99999'),
'is_confirmed': None,
'last_index': 1,
'nonce': Nonce(b'OOLRWTCMJMIQIAUVWBY9TVMZAJJ'),
'signature_message_fragment': Fragment(b'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'tag': Tag(b'HRIBEK999IOTA999TUTORIAL999'),
'timestamp': 1530080759,
'trunk_transaction_hash': TransactionHash(b'RRYDZLPS9IWCSWRROPQQJTKBRONOJNLUHVHFXFEOZAVGFEOUAHXWWHJSHAGWSSNSMWHN9SEYNBOEA9999'),
'value': 0}

广播

现在是时候将给定的bundle广播到网络的时候了。从前一步骤中返回的trytes中所有交易的列表是唯一需要的。

成功广播后,它将返回相同的输入bundle作为确认。

print("Broadcasting transaction...")
res = api.broadcast_and_store(att['trytes'])
pprint(res)

结果:

Broadcasting transaction...
{'trytes': [TransactionTrytes(b'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9999999999999999999999999999PVIBEK999IOTA999TUTORIAL999TFDCQZD99999999999A99999999TL9EHBGBIDCTLCARLPKBSUQIG9CKQPDGTLOVNLG9TALAGEWQRBEK9DPLDDBGOAZIREKGYPYFCWJCTOADWNNNY9QAGVOOGTCIGBBEPIYISUZKNJYUQMROBTZARQVPHONNLQNQFAHTBNXMYWDFHAHMNRJ9URLKX99999RRYDZLPS9IWCSWRROPQQJTKBRONOJNLUHVHFXFEOZAVGFEOUAHXWWHJSHAGWSSNSMWHN9SEYNBOEA9999HRIBEK999IOTA999TUTORIAL999RLISAKGKE999999999L99999999QRZIERPEJFLKTIVKEBQIHJXTAX9'),
TransactionTrytes(b'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUA9BDBWAZADBZACBSAXAVAABXAABWA99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW999999999999999999999999999HRIBEK999IOTA999TUTORIAL999TFDCQZD99A99999999A99999999TL9EHBGBIDCTLCARLPKBSUQIG9CKQPDGTLOVNLG9TALAGEWQRBEK9DPLDDBGOAZIREKGYPYFCWJCTOADWRRYDZLPS9IWCSWRROPQQJTKBRONOJNLUHVHFXFEOZAVGFEOUAHXWWHJSHAGWSSNSMWHN9SEYNBOEA9999KQBDDBHWHVLQSJFENGWRBHIOULOMC9GXDW99ECMVFHZXGFZQJOXATSMDLYQJFBZCXA9IJ9SQUXAFZ9999HRIBEK999IOTA999TUTORIAL999GDVSAKGKE999999999L99999999OOLRWTCMJMIQIAUVWBY9TVMZAJJ')]}

您可以通过The Tangle Explorer在任何接收地址上检查广播的bundle:

目标地址1: https://thetangle.org/address/CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9

目标地址2: https://thetangle.org/address/CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW

在一次调用中创建并广播交易(您希望快速发送交易)

如上所述,PyOTA库(以及几乎所有其他面向IOTA的库)都能够封装所有实现细节,因此不再需要关心它们。它通常基于IOTA基金会提出的扩展API调用,以便让开发者的工作变得更加轻松(https://github.com/iot/ger/wiki/blob/master/api-proposal.md)。

即使使用扩展的API调用,您仍然可以轻松的控制整个过程中的参与程度:

面向开发者、技术爱好者和初学者的IOTA开发要点

在设计你的应用时,你应该考虑一些细节:

  • 您正在失去对流程各个步骤的控制权,并且必须依赖一些预定义的参数(tip选择算法等)。
  • 如果在此过程中出现异常,则需要回到起点并重新开始整个过程。

所以基本上你可以决定什么最适合你的情况。

现在回到我们的练习。

代码基本上是在一次调用( send_transfer() )中广播交易,而且您不必关心任何隐藏在底层的实现细节。

import iota
from datetime import datetime
from pprint import pprint

MySeed = b"HGW9HB9LJPYUGVHNGCPLFKKPNZAIIFHZBDHKSGMQKFMANUBASSMSV9TAJSSMPRZZU9SFZULXKJ9YLAIUA"
TargetAddress1 = b"CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9"
TargetAddress2 = b"CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW"

NowIs = datetime.now() # get a actual date & time - just to have some meaningfull info

# preparing transactions
pt = iota.ProposedTransaction(address = iota.Address(TargetAddress1), # 81 trytes long address
message = iota.TryteString.from_unicode('Here comes a first message. Now is %s' % (NowIs)),
tag = iota.Tag(b'HRIBEK999IOTA999TUTORIAL'), # Up to 27 trytes
value = 0)

pt2 = iota.ProposedTransaction(address = iota.Address(TargetAddress2), # 81 trytes long address
message = iota.TryteString.from_unicode('Here comes a second message. Now is %s' % (NowIs)),
tag = iota.Tag(b'HRIBEK999IOTA999TUTORIAL'), # Up to 27 trytes
value = 0)
# besides the given attributes, library also adds a transaction timestamp

api = iota.Iota("https://field.carriota.com:443")

print("Preparing/Broadcasting... Wait please...")
# the whole process initiated in a single call
FinalBundle = api.send_transfer(depth=3,
transfers=[pt,pt2],
min_weight_magnitude=14)['bundle'] # it returns a dictionary with a bundle object

#bundle is broadcasted, let's print it
print("\nGenerated bundle hash: %s" % (FinalBundle.hash))
print("\nTail Transaction in the Bundle is a transaction #%s." % (FinalBundle.tail_transaction.current_index))

print("\nList of all transactions in the bundle:\n")
for txn in FinalBundle:
pprint(vars(txn))
print("")

结果:

Preparing/Broadcasting... Wait please...

Generated bundle hash: BUNIIHDTRGQYDBWHCELXHFXLUI9NJEGADGXDHHZIKIRULELTKVPTQJH9LJRVLBNVIIZKZJAGKRPHVQJAB

Tail Transaction in the Bundle is a transaction #0.

List of all transactions in the bundle:

{'_legacy_tag': Tag(b'GVIBEK999IOTA999TUTORIAL999'),
'address': Address(b'CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9'),
'attachment_timestamp': 1530093517465,
'attachment_timestamp_lower_bound': 0,
'attachment_timestamp_upper_bound': 12,
'branch_transaction_hash': TransactionHash(b'NEVBYHEKVOARHWETDYOBAJGWYQRTPH9Y9MJSRNPPYYSXGLLQECPLTSTFTJNUMTCQUPXLMYQ99FTNZ9999'),
'bundle_hash': BundleHash(b'BUNIIHDTRGQYDBWHCELXHFXLUI9NJEGADGXDHHZIKIRULELTKVPTQJH9LJRVLBNVIIZKZJAGKRPHVQJAB'),
'current_index': 0,
'hash': TransactionHash(b'FPGUNFXJGLLYRIBBCMPKGZSCPCQLB9XRWLUKEL9QLOGDTRPSNLRMDKFTNH9ASOCKTBK9XV9OTLQLZ9999'),
'is_confirmed': None,
'last_index': 1,
'nonce': Nonce(b'GCFAMCTYWHMOBXYOUAKKJL9AXKV'),
'signature_message_fragment': Fragment(b'RBTCFDTCEARCCDADTCGDEAPCEAUCXCFDGDHDEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUACBDBZABBDBWABBSAZAVAZA9BCB9B9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'tag': Tag(b'HRIBEK999IOTA999TUTORIAL999'),
'timestamp': 1530093508,
'trunk_transaction_hash': TransactionHash(b'AXLTSXPEWPGFIEBODUUYNEGGZAY9PHUTNBHBZMQUNYYYAMXJBDSQKSQWKTXFQCJXULVSNKYNEYFGZ9999'),
'value': 0}

{'_legacy_tag': Tag(b'HRIBEK999IOTA999TUTORIAL999'),
'address': Address(b'CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW'),
'attachment_timestamp': 1530093515168,
'attachment_timestamp_lower_bound': 0,
'attachment_timestamp_upper_bound': 12,
'branch_transaction_hash': TransactionHash(b'NEVBYHEKVOARHWETDYOBAJGWYQRTPH9Y9MJSRNPPYYSXGLLQECPLTSTFTJNUMTCQUPXLMYQ99FTNZ9999'),
'bundle_hash': BundleHash(b'BUNIIHDTRGQYDBWHCELXHFXLUI9NJEGADGXDHHZIKIRULELTKVPTQJH9LJRVLBNVIIZKZJAGKRPHVQJAB'),
'current_index': 1,
'hash': TransactionHash(b'AXLTSXPEWPGFIEBODUUYNEGGZAY9PHUTNBHBZMQUNYYYAMXJBDSQKSQWKTXFQCJXULVSNKYNEYFGZ9999'),
'is_confirmed': None,
'last_index': 1,
'nonce': Nonce(b'ELGHKLVUKKABDBYXIVPJVCMYRTP'),
'signature_message_fragment': Fragment(b'RBTCFDTCEARCCDADTCGDEAPCEAGDTCRCCDBDSCEAADTCGDGDPCVCTCSAEAXBCDKDEAXCGDEAWAUAVABBRAUA9BRAWAABEAUACBDBZABBDBWABBSAZAVAZA9BCB9B99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999'),
'tag': Tag(b'HRIBEK999IOTA999TUTORIAL999'),
'timestamp': 1530093508,
'trunk_transaction_hash': TransactionHash(b'NEVBYHEKVOARHWETDYOBAJGWYQRTPH9Y9MJSRNPPYYSXGLLQECPLTSTFTJNUMTCQUPXLMYQ99FTNZ9999'),
'value': 0}

您可以通过The Tangle Explorer在任何接收地址上检查广播的bundle:

目标地址1: https://thetangle.org/address/CXDUYK9XGHC9DTSPDMKGGGXAIARSRVAFGHJOCDDHWADLVBBOEHLICHTMGKVDOGRU9TBESJNHAXYPVJ9R9

目标地址2: https://thetangle.org/address/CYJV9DRIE9NCQJYLOYOJOGKQGOOELTWXVWUYGQSWCNODHJAHACADUAAHQ9ODUICCESOIVZABA9LTMM9RW

相关链接

这些开发笔记是由Petr Zizka(petr@zizkovi.name)开发和维护的。可以在TwitterIOTA Discord频道(@ hribek25#2683)关注作者。

项目Github链接:https://github.com/Hribek25/IOTA101

本文原文链接:https://hribek25.github.io/IOTA101/

inhuman

专栏作者:inhuman

个人简介:我共发表了 189 篇文章,总计被阅读了204,701 次,共获得了 1,758 个赞。

作者邮箱 作者主页 Ta的文章

2 条评论 “面向开发者、技术爱好者和初学者的IOTA开发要点”

发表评论

电子邮件地址不会被公开。 必填项已用*标注