可靠的交易提交
使用XRP账本的金融机构和其他服务应该使用此处描述的最佳实践来确保以可验证和快速的方式验证或拒绝交易。您应该将交易提交给受信任的(本地运营的)rippled服务器。
本文档中详述的最佳实践允许应用程序向XRP账本提交交易,同时实现:
未能实施最佳实践的应用程序存在以下错误风险:
这些类型的错误可能会导致严重的问题。例如,无法找到先前成功的付款交易的应用程序可能会错误地提交另一笔交易,以复制原始付款。这强调了应用程序基于权限交易结果的重要性,即使用本文档中描述的技术。
背景
XRP账本协议提供了在网络中的所有服务器之间共享的账本。通过协商一致和确认的过程,网络同意交易应用于(或从中省略)账本的顺序。
提交给信任的XRP Ledger服务器的格式良好的交易通常在几秒钟内被验证或拒绝。然而,有些情况下,一个格式良好的交易既没有得到验证,也没有被迅速拒绝。如果在应用程序发送交易后全局交易成本增加,则会发生一个特定情况。如果交易成本增加高于交易中指定的交易成本,则交易不包含在下一个经验证的账本中。如果在稍后某个日期全球交易成本降低,交易可以包含在后面的账本中。如果交易没有指定过期时间,则以上情形发生的时间就没有限制。
如果发生电力或网络中断,应用程序在查找提交的交易状态时面临更多挑战。应用程序必须注意正确提交交易并稍后妥善获取权威结果。
交易时间线
XRP Ledger提供了几个用于提交交易的rippledAPI,包括API和RippleAPI。无论使用的API如何,交易都会应用于账本。
注意:通过提交交易时rippled,从提交命令返回的成功状态码指示rippled服务器已收到候选交易。交易可能会或可能不会应用于经过验证的账本。
基于将候选交易应用于当前正在进行的账本的结果,API可能会返回临时结果。应用程序不得将这些与最终的,不可变的交易结果混淆。不可变的结果仅在经过验证的账本中找到。应用程序可能需要重复查询交易的状态,直到账本包含的交易结果被验证。
在应用交易时,rippled服务器使用上次验证的账本,基于整个网络已验证的交易的账本状态快照。达成共识和验证的过程将一组新的交易以规范的顺序应用于上一个经过验证的账本,从而产生一个新的经过验证的账本。这个新的经过验证的账本实例及其之前的账本实例形成账本历史记录。
每个经过验证的账本实例都有一个序号,比前一个实例的序号大1。每个账本也有一个识别的散列值,由其内容唯一确定。可能有许多不同版本的进行中的账本,它们具有相同的序列号但是不同的散列值。只有一个版本可以验证。
每个经过验证的账本都具有交易适用的权威订单。该订单基于账本的最终交易集来确定的。相比之下,每个rippled服务器上正在进行中的账本都会随着交易的接受而逐步运算。交易执行的临时顺序通常不同于以构建新的验证账本的交易执行的顺序。这是交易的临时结果可能与最终结果不同的原因之一。例如,付款可能会实现不同的最终汇率,具体取决于它是在使用相同报价的另一付款(消费挂单)之前或之后执行。
LastLedgerSequence
LastLedgerSequence是所有交易的可选参数。这将指示XRP账本必须在特定账本实例中或之前对交易进行验证。XRP账本不会在账本实例中包含序号高于交易LastLedgerSequence参数的交易。
使用该LastLedgerSequence参数可以防止交易未及时得到确认但可能包含在未来账本中的不良情况。您应该LastLedgerSequence在每个交易中指定参数。自动化流程应该使用比上次验证的账本索引大4的值,以确保交易以可预测且快速的方式进行验证或拒绝。
使用rippledAPI的应用程序应在提交交易时明确指定LastLedgerSequence。
RippleAPI使用maxLedgerVersion字段来指定LastLedgerSequence。RippleAPI默认自动提供适当的值。您可以指定maxLedgerVersion为null以故意忽略LastLedgerSequence,如果你想,一笔交易可以不限期的执行(这是强烈不推荐)。
最佳实践
可靠的交易提交
提交交易的应用程序应该使用以下实践以防止在过程中宕机或发生其他故障的情况下也可以可靠地提交。应用程序交易结果必须经过验证,以便应用程序可以对最终的验证结果采取行动。
提交和验证是两个独立的程序,可以使用本文档中描述的逻辑来实现。
提交
在提交之前持久化交易细节,以防在提交完成前发生电源故障或网络故障。重新启动时,持久值可以验证交易的状态。
提交过程:
验证
在正常操作期间,应用程序可以通过哈希来检查提交的交易的状态; 或者,根据使用的API,当交易已被验证(或失败)时接收通知。这种正常操作可能会中断,例如网络故障或电源故障。在这种中断的情况下,应用程序需要可靠地验证在中断之前可能提交或可能未提交到网络的交易的状态。
在重新启动时,或者确定新的最后验证账本(伪代码):
For each persistedtransaction without validated result:
Query transaction by hash
If (result appears invalidated ledger)
# Outcome is final
Persist the final result
If (result code istesSUCCESS)
Application may act based onsuccessful transaction
Else
The transaction failed (1)
If appropriate for the application and failure type, submitwith
new LastLedgerSequence and Fee
Elseif (LastLedgerSequence> newest validated ledger)
# Outcome is not yetfinal
Wait for more ledgers to bevalidated
Else
If (server has continuousledger history from the ledger when the
transaction was submitted up to and including the ledger
identified by LastLedgerSequence)
The transaction failed (2)
If appropriate for the application, submitwith
new LastLedgerSequence and Fee
Else
# Outcome is final, butnot known due to a ledger gap
Wait to acquire continuous ledgerhistory
失败案例
两个交易失败案例(在伪代码中标记为(1)和(2))之间的区别在于交易是否包含在经验证的账本中。在这两种情况下,您都应该认真决定如何处理失败。
账本空白
如果您的服务器没有从交易最初提交直至包括由确定的账本的持续账本历史记录LastLedgerSequence,那么您可能不知道交易的最终结果。(如果它包含在您的服务器缺失的一个账本版本中,你就不知道它是成功还是失败。)
您的rippled服务器应该有足够资源(CPU / RAM /磁盘IO)时自动获取缺失的账本版本,除非账本的版本数大于其配置的历史存储量。根据差距的大小和服务器的资源使用情况,获取缺失的账本应该花费几分钟的时间。您还可以在您的服务器上手动使用ledger_request方法发送请求获取历史账本版本。
或者,您可以使用rippled已经提供的存有全部账本历史记录的服务器(如Ripple的完整历史记录服务器s2.ripple.com)查看交易状态。达到此目的应该使用您信任的服务器。恶意服务器可能被编程来提供虚假交易状态和结果。
技术应用
要实现交易提交和验证最佳实践,应用程序需要执行以下操作:
应用程序如何执行这些操作取决于应用程序使用的API。应用程序可以使用以下任何接口:
Ripple - 提交和验证交易
rippled提供account_info方法来了解上一个经过验证的账本中的账户序列号。
JSON-RPC请求:
{
"method": "account_info",
"params": [
{
"account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"ledger": "validated"
}
]
}
响应体:
{
"result": {
"validated": true,
"status": "success",
"ledger_index": 10266396,
"account_data": {
"index": "96AB97A1BBC37F4F8A22CE28109E0D39D709689BDF412FE8EDAFB57A55E37F38",
"Sequence": 4,
"PreviousTxnLgrSeq": 9905632,
"PreviousTxnID": "CAEE0E34B3DB50A7A0CA486E3A236513531DE9E52EAC47CE4C26332CC847DE26",
"OwnerCount": 2,
"LedgerEntryType": "AccountRoot",
"Flags": 0,
"Balance": "49975988",
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
}
}
}
在本例中,截至最后一次验证账本(请求中的注释"ledger":"validated"和响应"validated": "true"),帐户的顺序为4(注释"Sequence":4,)。
如果应用程序要提交由此帐户签名的三个交易,他们将使用序列号4,5和6.要提交多个交易而不等待每个交易的验证,应用程序应该保留一个正在运行的帐户序列号。
该server_state方法返回最后确认账本的账本索引。
请求:
{
"id": "client id 1",
"method": "server_state"
}
响应:
{
"result": {
"status": "success",
"state": {
"validation_quorum": 3,
"validated_ledger": {
"seq": 10268596,
"reserve_inc": 5000000,
"reserve_base": 20000000,
"hash": "0E0901DA980251B8A4CCA17AB4CA6C3168FE83FA1D3F781AFC5B9B097FD209EF",
"close_time": 470798600,
"base_fee": 10
},
"server_state": "full",
"published_ledger": 10268596,
"pubkey_node": "n9LGg37Ya2SS9TdJ4XEuictrJmHaicdgTKiPJYi8QRSdvQd3xMnK",
"peers": 58,
"load_factor": 256000,
"load_base": 256,
"last_close": {
"proposers": 5,
"converge_time": 3004
},
"io_latency_ms": 2,
"fetch_pack": 10121,
"complete_ledgers": "10256331-10256382,10256412-10268596",
"build_version": "0.26.4-sp3-private"
}
}
}
在这个例子中,最后验证的账本序列号是10268596(result.state.validated_ledger在响应中找到)。还要注意这个例子表明在账本历史中存在差距。此处使用的服务器将无法提供有关在该间隔期间应用的交易的信息(账本10256383至10256411)。如果配置为这样,服务器最终将检索账本历史的那部分。
构建交易
rippled提供签名sign方法来准备要提交的交易。这种方法需要一个账户密钥,只能传递给可信rippled实例。此示例向另一个XRP账本地址发出10个FOO(补充货币)。
请求:
{
"method": "sign",
"params": [
{
"offline": true,
"secret": "sssssssssssssssssssssssssssss",
"tx_json": {
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"Sequence": 4,
"LastLedgerSequence":10268600,
"Fee": 10000,
"Amount": {
"currency": "FOO",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"value": "10"
},
"Destination": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"TransactionType": "Payment"
}
}
]
}
注意,应用程序指定了"Sequence":4从以前的呼叫中获知的帐户序列account_info,以避免tefPAST_SEQ错误。
另请注意,LastLedgerSequence基于我们的应用程序学习到的最后一次验证账本server_state。后端应用程序的建议是使用(上次验证的账本序列+4)。或者,使用(当前账本+ 3)的值。如果LastLedgerSequence计算错误且小于最后一次验证账本,交易将失败并显示tefMAX_LEDGER错误。
响应:
{
"result": {
"tx_json": {
"hash": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE57",
"TxnSignature": "304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C753",
"TransactionType": "Payment",
"SigningPubKey": "0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5",
"Sequence": 4,
"LastLedgerSequence": 10268600,
"Flags": 2147483648,
"Fee": "10000",
"Destination": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"Amount": {
"value": "10",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO"
},
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
},
"tx_blob": "12000022800000002400000004201B009CAFB861D4C38D7EA4C68000000000000000000000000000464F4F0000000000AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A68400000000000271073210267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC57446304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C7538114AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A831438BC6F9F5A6F6C4E474DB0D59892E90C2C7CED5C",
"status": "success"
}
}
应用程序应在提交之前保留交易的散列。该sign方法的结果包括哈希下tx_json。
提交交易
rippled提供了提交方法,允许我们提交已签名的交易。这使用tx_blob该sign方法返回的参数。
请求:
{
"method": "submit",
"params": [
{
"tx_blob": "12000022800000002400000004201B009CAFB861D4C38D7EA4C68000000000000000000000000000464F4F0000000000AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A68400000000000271073210267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC57446304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C7538114AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A831438BC6F9F5A6F6C4E474DB0D59892E90C2C7CED5C"
}
]
}
响应:
{
"result": {
"tx_json": {
"hash": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE57",
"TxnSignature": "304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C753",
"TransactionType": "Payment",
"SigningPubKey": "0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5",
"Sequence": 4,
"LastLedgerSequence": 10268600,
"Flags": 2147483648,
"Fee": "10000",
"Destination": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"Amount": {
"value": "10",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO"
},
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
},
"tx_blob": "12000022800000002400000004201B009CAFB861D4C38D7EA4C68000000000000000000000000000464F4F0000000000AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A68400000000000271073210267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC57446304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C7538114AC5FA3BB28A09BD2EC1AE0EED2315060E83D796A831438BC6F9F5A6F6C4E474DB0D59892E90C2C7CED5C",
"status": "success",
"engine_result_message": "The transactionwas applied.",
"engine_result_code": 0,
"engine_result": "tesSUCCESS"
}
}
这是一个初步结果。最终结果仅可从经过验证的账本中获得。缺少一个"validated":true字段表明这不是一个不可改变的结果。
验证交易
交易签名后生成的交易散列被传递给tx方法以检索交易结果。
请求:
{
"method": "tx",
"params": [
{
"transaction": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE57",
"binary": false
}
]
}
响应:
{
"result": {
"validated": true,
"status": "success",
"meta": {
"TransactionResult": "tesSUCCESS",
"TransactionIndex": 2,
"AffectedNodes": [...]
},
"ledger_index": 10268599[d],
"inLedger": 10268599,
"hash": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE57",
"date": 470798270,
"TxnSignature": "304402202646962A21EC0516FCE62DC9280F79E7265778C571E9410D795E67BB72A2D8E402202FF4AF7B2E2160F5BCA93011CB548014626CAC7FCBEBDB81FE8193CEFF69C753",
"TransactionType": "Payment",
"SigningPubKey": "0267268EE0DDDEE6A862C9FF9DDAF898CF17060A673AF771B565AA2F4AE24E3FC5",
"Sequence": 4,
"LastLedgerSequence": 10268600,
"Flags": 2147483648,
"Fee": "10000",
"Destination": "rawz2WQ8i9FdTHp4KSNpBdyxgFqNpKe8fM",
"Amount": {
"value": "10",
"issuer": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W",
"currency": "FOO"
},
"Account": "rG5Ro9e3uGEZVCh3zu5gB9ydKUskCs221W"
}
}
此示例响应显示"validated":true,指示交易已包含在验证的账本中,因此交易的结果是不可变的。此外,元数据包括"TransactionResult":"tesSUCCESS",指示交易已应用于账本。
如果回答不包括"validated":true,结果是暂时的,可能会有变化。为了检索最终结果,应用程序必须tx再次调用该方法,从而有足够的时间让网络验证更多账本实例。可能需要等待指定的账本LastLedgerSequence进行验证,但如果交易包含在先前验证过的账本中,那么结果在此时变得不可变。
验证缺少交易
应用程序必须处理对tx方法的调用返回txnNotFound错误的情况。
{
"result": {
"status": "error",
"request": {
"transaction": "395C313F6F11F70FEBAF3785529A6D6DE3F44C7AF679515A7EAE22B30146DE56",
"command": "tx",
"binary": false
},
"error_message": "Transaction notfound.",
"error_code": 24,
"error": "txnNotFound"
}
}
将txnNotFound出现在其中不包括在任何账本交易的情况下结果代码。但是,当rippled实例没有完整的账本历史时,或者交易尚未传播到rippled实例时,也可能发生此情况。应用程序应该进一步查询以确定如何作出反应。
该server_state方法(早期用来确定最后的验证台账)表示台账历史是如何完成的,下result.state.complete_ledgers。
{
"result": {
"status": "success",
"state": {
"validation_quorum": 3,
"validated_ledger": {
"seq": 10269447,
"reserve_inc": 5000000,
"reserve_base": 20000000,
"hash": "D05C7ECC66DD6F4FEA3A6394F209EB5D6824A76C16438F562A1749CCCE7EAFC2",
"close_time": 470802340,
"base_fee": 10
},
"server_state": "full",
"pubkey_node": "n9LJ5eCNjeUXQpNXHCcLv9PQ8LMFYy4W8R1BdVNcpjc1oDwe6XZF",
"peers": 84,
"load_factor": 256000,
"load_base": 256,
"last_close": {
"proposers": 5,
"converge_time": 2002
},
"io_latency_ms": 1,
"complete_ledgers": "10256331-10256382,10256412-10269447",
"build_version": "0.26.4-sp3-private"
}
}
}
我们的示例交易指定了LastLedgerSequence10268600,基于当时最后一次验证的账本,另加4。要确定我们缺少的交易是否永久失败,我们的rippled服务器必须有账本10268597到10268600.如果服务器在其历史记录中包含那些经过验证的账本,并且 tx返回txnNotFound,那么交易失败并且不能包含在任何未来账本中。在这种情况下,应用程序逻辑可以指定用相同的账户序列建立和提交替换交易并更新LastLedgerSequence。
服务器可能会报告上次验证的账本序号小于指定的序号LastLedgerSequence。如果是这样,则txnNotFound表示(a)所提交的交易尚未分配给网络,或(b)交易已分配给网络但尚未处理。为了处理前一种情况,应用程序可以再次提交相同的签名交易。由于交易具有唯一的帐户序号,因此最多只能处理一次。
最后,服务器可能会在交易历史记录中显示一个或多个空白。completed_ledgers上面的响应中显示的字段表示从此波动的实例中缺少账本10256383到10256411。我们的示例交易只能出现在账本10268597 - 10268600(基于何时提交LastLedgerSequence),因此这里显示的差额不相关。但是,如果差距表明该范围内的账本缺失,则应用程序需要查询另一个波动的服务器(或等待这一个检索缺失的账本)以确定txnNotFound结果是不可变的。
其他资源
XRP账本的一个重要和有意的特征是,一旦交易被纳入经过验证的账本,交易就是最终的。
但是,如果交易尚未包含在经过验证的账本中,则可以通过使其无效来有效取消该交易。通常,这意味着Sequence
从同一个帐户中发送具有相同价值的另一个交易。如果您不希望替换交易执行任何操作,请发送没有选项的AccountSet交易。
例如,如果您尝试提交序列号为11,12和13的3个交易,但交易11以某种方式丢失或没有足够高的交易成本传播到网络,则可以通过提交来取消交易11通过发送序列号为11的没有选项的AccountSet交易。这不做任何事(除了销毁新交易11的交易成本外),但它允许交易12和13变得有效。
这种方法比重新编号和重新提交交易12和13更可取,因为它可以防止交易在不同序列号下被有效复制。
这样,没有选项的AccountSet交易就是 "no-op" 交易。
要查看交易的最终结果,请使用tx方法,account_tx方法或其他rippled
的返回结果
。寻找"validated": true
以表明此响应使用已经通过一致性验证的账本版本。
Field |
值 |
描述 |
meta.TransactionResult |
String |
将结果分类的代码,例如 |
validated |
Boolean |
这个结果是否来自验证账本。如果 |
"hash"
:
"E08D6E9754025BA2534A78707605E0601F03ACE063687A0CA1BDDACFCD1698C7",
"meta"
: {
...
"TransactionResult"
:
"tesSUCCESS"
},
"validated"
:
true
总结:
Ripple最新账本包含的交易并不一定是成功的交易,对于第三方应用来说,需要验证账本的validated及交易返回结果。
Rippple的服务器不一定包含全部的历史账本,可以配置下保存一定量的历史账本,若因某些原因服务器历史账本不全可以使用命令获取账本,或直接调用ripple的历史服务器节点获取。
本文作者:architect.bian,欢迎收藏,转载请保留原文地址并保留版权声明!谢谢~
还没完!往下看!!!