生产、资源与运输的职责划分
当你的游戏或模型里有会耗尽的存量,比如粮食、布匹、弹药或建材,就读这一页。它说明每一步由哪个 crate 负责、各步骤的先后顺序,以及需求来源、动用授权、损耗、交换、本地交接、生产产出和自定义消费者的规则。
这些能力由可选的 canwu-resource 和 canwu-production 扩展提供。模拟内核保持通用:粮食、工厂、价格、历史矿点和军队都放在扩展、内容和你的应用中。
| 负责方 | 负责内容 |
|---|---|
| 模拟内核 | 命令准入、确定性时间、阶段顺序、原子提交、回滚、可见性、存档与重放 |
canwu-resource |
资源与单位版本、账户(ResourceAccount)、受保护存量底线、需求(ResourceDemand)、资源预留(ResourceReservation)、分配腿、转移、消费、损耗和履约 |
canwu-production |
工艺、生产地点、设施、产能、工单、在制品、执行、项目和产出结算。它使用资源结果和技术证据,自己不保存库存。 |
canwu-transport 与 canwu-movement |
行程、路段、保管交接和交付完成;canwu-movement 负责移动执行和运力池的生命周期。货物余额、损耗、验收、退回和目的地入账仍由 canwu-resource 负责。 |
| 你的人口、军事等领域 | 形成需求,并对履约结果作出反应 |
| 你的应用 | 货币、市场、工资、建筑界面、战斗公式和历史内容 |
canwu-resource 是库存余额的唯一写入者。其他领域提交资源操作并读取结果,不得直接写余额,也不得改写其他领域的记录。这些 crate 都建立在对外 canwu-api 之上。
| 术语 | 含义 |
|---|---|
| 账户 | 某一确切资源与单位版本的一份库存余额(balance),由保管人持有;保管人即对这份库存负责的持有人。 |
| 受保护存量底线 | 账户不参与普通分配的保留量,例如种粮(ProtectedFloorPolicyRevision)。需求的 protection_override_class 若列在该策略中,可以动用底线以下的存量。 |
| 需求 | 申请者对一定数量的请求,带有最低有用量、部分履约策略、到期和失效时间、优先级以及来源策略。 |
| 资源预留与分配腿 | 分配的结果:在某个账户中为某项需求留出的数量。预留被释放或过期后,这部分数量重新可用。 |
| 转移托管量 | 已离开来源账户、正在途中的存量。在被验收、损耗或退回之前,它一直计入总量。 |
| 完成租约 | 持有人执行一项不可逆操作的权利,以操作键标识。激活租约得到 CompletionLeaseActivationCertificateV1,它固定操作键、时间和操作所依赖的精确记录。不可逆请求都要携带这份凭证。 |
| 适配器输入 | 提供方插件使用的输入路径(enqueue_resource_adapter_operation)。每项操作都要引用证明其合法的提供方精确记录版本。 |
带请求标识的命令指通过 CommandRequest(含请求标识和预期修订号)提交的命令。资源命令用 resource_command(&ResourceCommandV1 { subject, request }) 构建,签发者必须能代表 subject。
查看图表源码
flowchart TB
Demand["需求:申请者提交 SubmitDemand 命令"] --> Alloc["分配:上层应用排入 enqueue_resource_allocation,canwu-resource 预留存量"]
Alloc --> Consume["消费:消费者插件经适配器输入"]
Alloc --> Begin["BeginTransfer:来源保管人或被授权方提交命令,存量进入托管"]
Begin --> Move["运输:canwu-transport 与 canwu-movement"]
Move --> Accept["经适配器输入 Accept"]
Begin -. "相同 place_scope" .-> Local["AcceptLocal:目的地保管人"]
Move --> LoseReturn["Lose 或 Return:转移控制方"]
Accept --> Credit["目的地账户入账"]
Local --> Credit
Credit --> Later["之后的结算边界:消费者使用存量"]
- 需求。 申请者以带请求标识的资源命令提交
SubmitDemand,之后可用AmendDemand或CancelDemand修改或取消。适配器输入不能提交或修改需求。 - 分配。 上层应用用
enqueue_resource_allocation为某个申请者排入一次分配。canwu-resource依据受保护存量底线、最低有用量、部分履约策略、到期时间、优先级和稳定的 tie-break 键做确定性分配,并记录资源预留和分配腿。 - 扣减。 消费者插件经适配器输入消费分配结果(
Consume),或者由来源保管人(授权下的分配则为被授权方)发起转移(BeginTransfer),把相应数量移入托管。 - 运输。
canwu-transport和canwu-movement负责运送。转移进度(AdvanceTransfer)经适配器输入提交,并引用运输证据。 - 到达。
CompleteTransfer结束转移:经适配器输入的Accept为目的地入账;AcceptLocal处理同地交接;其余情况用Lose或Return结束。 - 使用。 生产、军事补给等消费者在之后的结算边界使用已入账的存量。它们引用精确的分配与履约证据,只写自己的记录。
转移的每一步都要给出 expected_transfer_revision,过期或重复的步骤会被拒绝,同一次到货不会入账两次。守恒、版本或权限校验任一失败,该操作的全部变化一起回滚。
canwu-resource 用 ResourceState::validate_conservation 按 ConservationTotalsV1 检查守恒:
期末余额 + 期末转移托管量 = 期初余额 + 期初托管量 + 已准入产出 + 外部流入 - 已准入消费 - 已准入损耗 - 外部流出只有 balance 是存储值;可用量、预留量和受保护量都是派生出来的(ResourceState::account_quantities)。路线估算、到货验收和消费是三件独立的事,各自需要单独的步骤。
可运行的十四个月粮食循环:执行 cargo run -p canwu-economy-reference --example grain_loop,并参阅生产、资源与军事补给案例。
需求来源策略
Section titled “需求来源策略”每个 ResourceDemand 都保存一个 source_policy(ResourceDemandSourcePolicyV1),决定哪些账户可以为它供给:
| 策略 | 可供给的账户 |
|---|---|
Pooled(默认) |
资源版本和单位版本都匹配的所有开放账户,按账户 ID 顺序访问 |
ExactAccounts(accounts) |
只限列出的账户。每个账户必须存在、未关闭、两个版本都匹配,且保管人是申请者本人。 |
Granted { grant_id, accounts } |
只限列出的账户。每个账户都必须由所指动用授权的授权方保管,且申请者必须是该授权的被授权方(见动用授权)。 |
规则:
ExactAccounts和Granted的列表含 1 到 256 个账户(MAX_DEMAND_SOURCE_ACCOUNTS),ID 严格递增、不能重复。列表无效时,命令在任何存量变化之前就被拒绝;分配时还会再检查一次。- 分配只根据所选账户计算可用量、最低有用量和部分履约。列出的账户不够时,需求只能拿到这些账户里的存量。受保护存量底线以及之后的转移、消费校验照常适用。
- 在需求第一次分配之前,可以凭预期需求修订号修改策略。一旦有了任何资源预留(包括已消费、但仍对应在途转移的预留)或任何履约,策略就固定下来;要换策略,请取消需求再提交新需求。取消需求不会影响它已经发起的转移。
AmendDemand不能修改需求的状态、拒绝原因或申请者,这类修改会被记录为拒绝。已终止的需求不能修改。
权限:Pooled 和 ExactAccounts 只决定从哪里取得供给,不赋予动用其他保管人存量的权利。由你的应用控制谁可以提交资源池需求、以哪个申请者身份提交。要让一个保管人在限额和时间窗口内动用另一方的存量,请使用动用授权和 Granted 策略。任命、用途以及谁可以授权给谁,仍由你的应用决定。
持久化:策略进入请求摘要、运行时快照、精确重放和已终止需求的归档。持有人相对报告不会公开来源列表。缺少 source_policy 的 JSON 需求会反序列化为 Pooled。
动用授权(ResourceAccessGrantV1)让被授权方无需先转移,就能动用授权方保管的存量。它记录:
grantor_custodian(授权方保管人)和grantee(被授权方);- 精确的
resource_revision与unit_revision; cap_quantity,这份授权最多能供给的总量;- 有效期
valid_from..valid_until(不含终点); authority_evidence,证明这份授权成立的精确记录版本,例如应用中已被接受的征发记录。
授权方以带请求标识的命令签发授权:
use canwu_api::{CommandEnvelope, CommandRequest, CommandRequestId, Issuer};use canwu_resource::{ ResourceCommandV1, ResourceIssueAccessGrantRequestV1, ResourceOperationRequestV1, resource_command,};
let command = resource_command(&ResourceCommandV1 { subject: grant.grantor_custodian.clone(), request: ResourceOperationRequestV1::IssueAccessGrant(ResourceIssueAccessGrantRequestV1 { operation_key, grant, // 一个 ResourceAccessGrantV1 }),})?;canwu.enqueue_command( now, 0, CommandRequest::new( CommandRequestId::new(41), canwu.revision(), CommandEnvelope::new(Issuer::Actor(grantor_person), command).at_time(now), ),)?;授权的使用过程:
- 签发。 授权方保管人以带请求标识的命令提交
IssueAccessGrant。权限证据必须是可用的精确记录版本。适配器输入不能签发或撤销授权。 - 提出需求。 被授权方提交来源策略为
ResourceDemandSourcePolicyV1::Granted { grant_id, accounts }的需求。授权必须有效且被授权方就是申请者;资源和单位必须匹配;需求的到期和失效时间必须落在授权有效期内;列出的每个账户都必须开放并由授权方保管。 - 分配。 分配在多个需求之间分摊紧缺存量之前,先检查授权。已撤销或不在有效期内的授权不提供任何供给,任何分配都不会超过剩余额度。供给只来自列出的账户。
- 扣减。 被授权方在自己的完成租约下扣减分配结果,方式可以是消费,也可以是发起转移或交换的一条腿。其他任何人(包括授权方)持有的租约都不能扣减授权下的分配。扣减只在时间等于租约认证时间的结算边界结算,且该时间必须位于授权有效期内。
额度核算:ResourceAccessGrantRecordV1 始终满足 cap_quantity = remaining + reserved + debited。分配把数量从 remaining 移到 reserved,扣减再移到 debited,每个单位只计一次。预留被释放或过期时,数量回到 remaining。授权需求中过期的预留,会在为被授权方运行的下一次分配中释放;这次分配由上层应用通过 enqueue_resource_allocation 排入。
转移:由授权下的分配发起的转移会记录其 access_grant,之后由被授权方控制它的取消、退回和损耗。本地验收仍归目的地保管人。
撤销:授权方凭预期授权修订号提交 RevokeAccessGrant。授权下一旦有过任何预留或扣减,撤销就会被拒绝,所以额度和有效期要按实际委托来定。
上限与读取:授权(包括已撤销的)一直保存在热状态中,每个资源状态最多 4,096 份(MAX_RESOURCE_ACCESS_GRANTS)。要像控制账户创建一样控制谁能签发授权。resource_access_grant_status(canwu, holder, grant_id) 向授权方或被授权方返回授权内容及其额度核算。
举个例子:应用中的征发流程先记录人口所有者的同意;所有者再引用这条记录签发授权,军队提交 Granted 需求,并用自己的租约扣减分配结果。动用授权测试通过对外 API 演示了签发授权、在授权下分配、以转移方式扣减以及撤销授权。
ResourceOperationRequestV1::RecordLoss(ResourceAccountLossRequestV1) 直接在单个账户上记录腐坏、失窃或其他损耗。请求携带 loss_id、账户的预期修订号、数量、cause、allow_protected、时间和完成凭证。
- 它结算为
account: Some(..)、transfer: None的ResourceLoss,在守恒总量中计为已准入损耗。 - 扣减不动用已预留的存量;除非设置了
allow_protected,否则遵守受保护存量底线。账户修订号过期会被拒绝。 - 作为带请求标识的命令时,必须来自账户保管人。经适配器输入时,
cause必须是提供方的精确来源记录。 - 持有人报告会包含
ResourceLossObservationV1条目,可见性规则与转移详情相同。
ResourceOperationRequestV1::BeginExchange(ResourceExchangeStartRequestV1) 同时发起两项转移:要么都创建,要么都不创建。无论成功还是被拒绝,唯一的结果都会在 cited_transfers 中列出两个转移 ID。
- 双方先约定
ResourceExchangeTermsV1:交换的操作键,以及每条腿的转移 ID、精确分配和目的地。 ResourceExchangeTermsV1::leg_operation_keys根据这些条款的摘要派生每条腿的操作键。- 每条腿的来源保管人(授权下的腿则为被授权方)为该腿的派生键取得完成租约。这份租约因此代表对整个交换的同意,换成其他条款就用不上。
leg_a的扣减方把交换作为带请求标识的命令提交。
交换本身没有单独的凭证。发起之后,两项转移各自结算,可能一项被验收、另一项损耗或退回。每一方动用的都只是自己保管的、或经授权可以动用的存量。
ResourceTransferDispositionV1::AcceptLocal { destination, expected_destination_revision, handover_evidence } 不经运输执行就结算一项转移,例如同一仓库或同一城镇内的交接。它只适用于仍处于 PendingDispatch、没有运输关联的转移,且两个账户声明了相同的 ResourceAccount::place_scope。
place_scope由场景在安装账户时声明,之后保持不变,参伍不做推断。默认值None表示该账户不能使用本地验收。带请求标识的CreateAccount命令不能设置它。- 地点范围缺失或不一致时以
invalid_definition拒绝;已有运输关联的转移以invalid_lifecycle拒绝。 - 终结凭证会锁定精确的交接记录。作为带请求标识的命令时,
AcceptLocal必须来自目的地保管人;经适配器输入时,交接记录必须是提供方的来源记录。
ProductionOperation::CompleteExecution 可以携带 realized_output_per_mille 和 realization_evidence。比例以工艺名义产出的千分之一为单位,None 表示 1,000。执行完成时,每项产出结算数量按该比例缩放并向下取整,两个值都保存在生产执行上;资源入账和产出确认随后按缩放后的数量精确结算。
- 比例不等于 1,000 时,需要一个精确证据记录版本,其种类必须列在工艺版本的
realization_evidence_kinds中。该列表默认为空,所以默认工艺只接受名义产出。名义比例附带证据会被拒绝。 - 比例必须为正,且不能把任何一条产出腿缩放为零。要记录完全损失,请取消工单。
- 比例不能超过工艺版本的
max_realized_per_mille,默认值为 1,000;调高它即可接受有证据支持的超名义产出。 - 持有人、生命周期、比例和种类规则都在查找证据记录之前检查,因此拒绝结果不会透露其他记录是否存在。
产出如何结算
Section titled “产出如何结算”执行完成后,canwu-production 的一个第 12 阶段系统把当前生产运行时记录的版本固定为该执行的 output_source,并向 canwu-resource 排入一个产出批次。资源插件通过生产产出批次输入为各产出账户入账,并要求入账引用的正是这个固定来源。通用适配器输入会拒绝生产入账。
授权自定义消费者
Section titled “授权自定义消费者”你自己的消费者插件可以通过 ResourceConsumptionIntentV1 契约消费分配结果:
- 在插件自己的有效领域记录中,保存一个顶层
resource_consumption_intents映射,每个键等于对应已封存意图的id。无需回调,也无需借用参考提供方的名称。 - 把这个记录种类传给
ResourcePlugin::new,让资源适配器可以读取精确来源。 - 提交之前,先完成分配和完成租约的各个步骤。
- 经适配器输入(
enqueue_resource_adapter_operation)提交ResourceOperationRequestV1::Consume。其consumer_evidence必须等于提供方当前的精确来源记录,完成凭证必须锁定该来源并使用同一操作键。持有人、参与者、时间、修订号和租约的常规校验照常进行。
只有当映射中恰好一个 Authorized 意图与完整的分配腿、需求和账户修订号、消费 ID 以及操作键全部匹配时,资源插件才接受这次消费。只消费一条分配腿的一部分,不在这个契约范围内。映射最多容纳 max_operation_outcomes 个条目(取自资源状态的 ResourceLimitsV1),超出时在解码任何条目之前就被拒绝。映射缺失或格式错误、标识或摘要不符,以及意图已退役、有歧义或不匹配时,都会被拒绝,不发生任何扣减。封存摘要只证明意图内容前后一致;许可来自你的插件对意图的授权。
意图的授权与退役,以及资源回执之后的本领域后果,都由你的插件负责;守恒余额和重复操作的处理由 canwu-resource 负责。军事补给参考消费者(canwu-force-supply-reference)保留自己的专用适配器,也接受其来源记录的较早版本;自定义消费者使用上面的当前来源契约。
请从同一发布线精确固定 canwu-api、canwu-resource、canwu-production 的版本和插件语义哈希。加载时恢复存档中的参伍快照,并按你传入的插件重新验证扩展记录。另见存档兼容性。
继续阅读生产、资源与军事补给案例与机制决议。