github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-supply-chain-master/docs/source/family_specification.rst (about) 1 ****************************************************** 2 Sawtooth Supply Chain Transaction Family Specification 3 ****************************************************** 4 5 Overview 6 ======== 7 8 The Sawtooth Supply Chain transaction family allows users to track 9 goods as they move through a supply chain. Records for goods include a 10 history of ownership and custodianship, as well as histories for a 11 variety of properties such as temperature and location. These 12 properties are managed through a user-specifiable system of record 13 types. 14 15 16 State 17 ===== 18 19 All Supply Chain objects are serialized using Protocol Buffers before being 20 stored in state. These objects include: Agents, Properties 21 (accompanied by their auxiliary PropertyPage objects), Proposals, 22 Records, and RecordTypes. As described in the Addressing_ section 23 below, these objects are stored in separate sub-namespaces under the 24 Supply Chain namespace. To handle hash collisions, all objects are stored in 25 lists within protobuf "Container" objects. 26 27 28 Records 29 ------- 30 31 Records represent the goods being tracked by Supply Chain. Almost 32 every transaction references some Record. 33 34 A Record contains a unique identifier, the name of a RecordType, and 35 lists containing the history of its owners and custodians. It also 36 contains a ``final`` flag indicating whether further updates can be 37 made to the Record and its Properties. If this flag is set to true, 38 then no further updates can be made to the Record, including changing 39 its ``final`` flag. 40 41 .. code-block:: protobuf 42 43 message Record { 44 message AssociatedAgent { 45 // The Agent's public key 46 string agent_id = 1; 47 48 // Approximately when this agent was associated, as a Unix UTC timestamp 49 uint64 timestamp = 2; 50 } 51 52 // The unique user-defined natural key which identifies the 53 // object in the real world (for example, a serial number) 54 string identifier = 1; 55 56 string record_type = 2; 57 58 // Ordered oldest to newest by timestamp 59 repeated AssociatedAgent owners = 3; 60 repeated AssociatedAgent custodians = 4; 61 62 // Flag indicating whether the Record can be updated. If it is set 63 // to true, then the record has been finalized and no further 64 // changes can be made to it or its Properties. 65 bool final = 5; 66 } 67 68 69 Note that while information about a Record's owners and custodians are 70 included in the object, information about its Properties are stored 71 separately (see the Properties_ section below). 72 73 Records whose addresses collide are stored in a list alphabetically by 74 identifier. 75 76 .. code-block:: protobuf 77 78 message RecordContainer { 79 repeated Record entries = 1; 80 } 81 82 .. _Properties: 83 84 Properties 85 ---------- 86 87 Historical data pertaining to a particular data field of a tracked 88 object are stored as Properties, represented as a list of values 89 accompanied by a timestamp and a reporter identifier. 90 91 The whole history of updates to Record data is stored in current state 92 because this allows for more flexibility in writing transaction rules. 93 For example, in a fish track-and-trade system, there might be a rule 94 that no fish can be exchanged whose temperature has gone above 40 95 degrees. This means, however, that it would be impractical to store 96 all of a Record's data at one address, since adding a single update 97 would require reading the entire history of each of the Record's 98 Properties out of state, adding the update, then writing it all back. 99 100 To solve this problem, Properties are stored in their own namespace 101 derived from their name and associated Record. Since some Properties 102 may have thousands of updates, four characters are reserved at the end 103 of that namespace in order to paginate a Property's history. The 104 Property itself (along with name, Record identifier, authorized 105 reporters, and paging information) is stored at the namespace ending 106 in ``0000``. The namespaces ending in ``0001`` to ``ffff`` will each 107 store a PropertyPage containing up to 256 reported values (which 108 include timestamps and their reporter's identity). Any Transaction 109 updating the value of a Property first reads out the PropertyContainer 110 object at ``0000`` and then reads out the appropriate 111 PropertyPageContainer before adding the update and writing the new 112 PropertyPageContainer back to state. 113 114 The Transaction Processor treats these pages as a ring buffer, so that 115 when page ``ffff`` is filled, the next update will erase the entries 116 at page ``0001`` and be stored there, and subsequent page-filling will 117 continue to overwrite the next oldest page. This ensures no Property 118 ever runs out of space for new updates. Under this scheme, 16^2 * 119 (16^4 - 1) = 16776960 entries can be stored before older updates are 120 overwritten. 121 122 Updates to Properties can have one of the following protobuf types: 123 ``bytes``, ``string``, ``sint64``, ``float``, or ``Location`` (see the 124 section on RecordTypes_ below). The type of an update is indicated by 125 a tag belonging to the PropertySchema object. 126 127 .. code-block:: protobuf 128 129 message Property { 130 message Reporter { 131 // The public key of the Agent authorized to report updates. 132 string public_key = 1; 133 134 // A flag indicating whether the reporter is authorized to 135 // send updates. When a reporter is added, this is set to 136 // true, and a `RevokeReporter` transaction sets it to false. 137 bool authorized = 2; 138 139 // An update must be stored with some way of identifying which 140 // Agent sent it. Storing a full public key for each update would 141 // be wasteful, so instead Reporters are identified by their index 142 // in the `reporters` field. 143 uint32 index = 3; 144 } 145 146 // The name of the Property, e.g. "temperature". This must be unique 147 // among Properties. 148 string name = 1; 149 150 // The natural key of the Property's associated Record. 151 string record_id = 2; 152 153 // The Property's type (int, string, etc.) 154 PropertySchema.DataType data_type = 3; 155 156 // The Reporters authorized to send updates, sorted by index. New 157 // Reporters should be given an index equal to the number of 158 // Reporters already authorized. 159 repeated Reporter reporters = 4; 160 161 // The page to which new updates are added. This number represents 162 // the last 4 hex characters of the page's address. Consequently, 163 // it should not exceed 16^4 = 65536. 164 uint32 current_page = 5; 165 166 // A flag indicating whether the first 16^4 pages have been filled. 167 // This is used to calculate the last four hex characters of the 168 // address of the page containing the earliest updates. When it is 169 // false, the earliest page's address will end in "0001". When it is 170 // true, the earliest page's address will be one more than the 171 // current_page, or "0001" if the current_page is "ffff". 172 bool wrapped = 6; 173 } 174 175 message PropertyPage { 176 message ReportedValue { 177 // The index of the reporter id in reporters field 178 uint32 reporter_index = 1; 179 // Approximately when this value was reported, as a Unix UTC timestamp 180 uint64 timestamp = 2; 181 182 // The type-specific value of the update. Only one of these 183 // fields should be used, and it should match the type 184 // specified for this Property in the RecordType. 185 bytes bytes_value = 11; 186 string string_value = 12; 187 sint64 int_value = 13; 188 float float_value = 14; 189 Location location_value = 15; 190 } 191 192 // The name of the page's associated Property and the record_id of 193 // its associated Record. These are required to distinguish pages 194 // with colliding addresses. 195 string name = 1; 196 string record_id = 2; 197 198 // ReportedValues are sorted first by timestamp, then by reporter_index. 199 repeated ReportedValue reported_values = 4; 200 } 201 202 203 Properties and PropertyPages whose addresses collide are stored in 204 lists alphabetized by Property name. 205 206 .. code-block:: protobuf 207 208 message PropertyContainer { 209 repeated Property entries = 1; 210 } 211 212 message PropertyPageContainer { 213 repeated PropertyPage entries = 1; 214 } 215 216 .. _RecordTypes: 217 218 Record Types 219 ------------ 220 221 In order to validate incoming tracking data, Records are assigned a 222 RecordType at creation. A RecordType is a user-defined list of 223 PropertySchemas, each of which has a name and data type. 224 PropertySchemas may be designated as ``required``. A required Property 225 must be initialized with a value at the time of a Record's creation. 226 For example, a ``Fish`` type might list ``species`` as required, but 227 not ``temperature``, since temperature wouldn't be known until 228 measurements were taken. Properties not specified at Record creation 229 are initialized as empty lists. 230 231 .. code-block:: protobuf 232 233 message PropertySchema { 234 enum DataType { 235 BYTES = 0; 236 STRING = 1; 237 INT = 2; 238 FLOAT = 3; 239 LOCATION = 4; 240 } 241 242 // The name of the property, e.g. "temperature" 243 string name = 1; 244 245 // The Property's type (int, string, etc.) 246 DataType data_type = 2; 247 248 // A flag indicating whether initial values must be provided for the 249 // Property when a Record is created. 250 bool required = 3; 251 } 252 253 254 message RecordType { 255 // A unique human-readable designation for the RecordType 256 string name = 1; 257 258 repeated PropertySchema properties = 2; 259 } 260 261 262 Each Record will have exactly the Properties listed in its type. New 263 Records cannot be created without a type; consequently, a 264 type-creation transaction must be executed before any Records can be 265 created. 266 267 RecordTypes whose addresses collide are stored in a list alphabetized 268 by name. 269 270 .. code-block:: protobuf 271 272 message RecordTypeContainer { 273 repeated RecordType entries = 1; 274 } 275 276 277 Because it is expected to be used for many RecordTypes, a dedicated 278 Location protobuf message is used, the values of which are latitude 279 and longitude. 280 281 .. code-block:: protobuf 282 283 message Location { 284 // Coordinates are expected to be in millionths of a degree 285 sint64 latitude = 1; 286 sint64 longitude = 2; 287 } 288 289 290 Agents 291 ------ 292 293 Agents are entities that can send transactions affecting Records. This 294 could include not only humans and companies that act as owners and 295 custodians of objects being tracked, but also autonomous sensors 296 sending transactions that update Records' data. All Agents must be 297 created (registered on-chain) before interacting with Records. 298 299 .. code-block:: protobuf 300 301 message Agent { 302 // The Agent's public key. This must be unique. 303 string public_key = 1; 304 305 // A human-readable name identifying the Agent. 306 string name = 2; 307 308 // Approximately when the Agent was registered, as a Unix UTC timestamp 309 uint64 timestamp = 3; 310 } 311 312 Agents whose keys have the same hash are stored in a list alphabetized 313 by public key. 314 315 .. code-block:: protobuf 316 317 message AgentContainer { 318 repeated Agent entries = 1; 319 } 320 321 322 Proposals 323 --------- 324 325 A Proposal is an offer from the owner or custodian of a Record to 326 authorize another Agent as an owner, custodian, or reporter for that 327 Record. Proposals are tagged as being for transfer of ownership, 328 transfer of custodianship, or authorization of a reporter for some 329 Properties. Proposals are also tagged as being open, accepted, 330 rejected, or canceled. There cannot be more than one open Proposal for 331 a specified role for each combination of Record, receiving Agent, and 332 issuing Agent. 333 334 .. code-block:: protobuf 335 336 message Proposal { 337 enum Role { 338 OWNER = 1; 339 CUSTODIAN = 2; 340 REPORTER = 3; 341 } 342 343 enum Status { 344 OPEN = 1; 345 ACCEPTED = 2; 346 REJECTED = 3; 347 CANCELED = 4; 348 } 349 350 // The id of the Record with which this Proposal deals 351 string record_id = 1; 352 353 // Approximately when this proposal was created, as a Unix UTC timestamp 354 uint64 timestamp = 2; 355 356 // The public key of the Agent that created the Proposal 357 string issuing_agent = 3; 358 359 // The public key of the Agent to which the Proposal is addressed 360 string receiving_agent = 4; 361 362 // Whether the Proposal is for transfer of ownership or 363 // custodianship or reporter authorization 364 Role role = 5; 365 366 // The names of properties for which the reporter is being authorized 367 // (empty for owner or custodian transfers) 368 repeated string properties = 6; 369 370 // Whether the Proposal is open, accepted, rejected, or canceled. 371 // For a given Record and receiving Agent, there can be only one 372 // open Proposal at a time for each role. 373 Status status = 7; 374 375 // human-readable terms of transfer 376 string terms = 8; 377 } 378 379 380 Proposals with the same address are stored in a list sorted 381 alphabetically first by ``record_id``, then by ``receiving_agent``, 382 then by ``timestamp`` (earliest to latest). 383 384 .. code-block:: protobuf 385 386 message ProposalContainer { 387 repeated Proposal entries = 1; 388 } 389 390 .. _Addressing: 391 392 Addressing 393 ---------- 394 395 Supply Chain objects are stored under the namespace obtained by taking the 396 first six characters of the SHA-512 hash of the string 397 ``supply_chain``: 398 399 .. code-block:: pycon 400 401 >>> def get_hash(string): 402 ... return hashlib.sha512(string.encode('utf-8')).hexdigest() 403 ... 404 >>> get_hash('supply_chain')[:6] 405 '3400de' 406 407 After its namespace prefix, the next two characters of a Supply Chain object's 408 address are a string based on the object's type: 409 410 - Agent: ``ae`` 411 - Property / PropertyPage: ``ea`` 412 - Proposal: ``aa`` 413 - Record: ``ec`` 414 - Record Type: ``ee`` 415 416 The remaining 62 characters of an object's address are determined by 417 its type: 418 419 - Agent: the first 62 characters of the hash of its public key. 420 - Property: the concatenation of the following: 421 422 - The first 36 characters of the hash of the identifier of its 423 associated Record plus the first 22 characters of the hash of its 424 Property name. 425 - The string ``0000``. 426 427 - PropertyPage: the address of the page to which updates are to be 428 written is the concatenation of the following: 429 430 - The first 36 characters of the hash of the identifier of its 431 associated Record. 432 - The first 22 characters of the hash of its Property name. 433 - The hex representation of the ``current_page`` of its associated 434 Property left-padded to length 4 with 0s. 435 436 - Proposal: the concatenation of the following: 437 438 - The first 36 characters of the hash of the identifier of 439 its associated Record. 440 - The first 22 characters of its ``receiving_agent``. 441 - The first 4 characters of the hash of its ``timestamp``. 442 443 - Record: the first 62 characters of the hash of its identifier. 444 - Record Type: the first 62 characters of the hash of the name of the 445 type. 446 447 For example, if ``fish-456`` is a Record with a ``temperature`` 448 Property and a ``current_page`` of 28, the address for that 449 PropertyPage is: 450 451 .. code-block:: pycon 452 453 >>> get_hash('supply_chain')[:6] + 'ea' + get_hash('fish-456')[:36] + get_hash('temperature')[:22] + hex(28)[2:].zfill(4) 454 '3400deea840d00edc7507ed05cfb86938e3624ada6c7f08bfeb8fd09b963f81f9d001c' 455 456 457 Transactions 458 ============ 459 460 Transaction Payload 461 ------------------- 462 463 All Supply Chain transactions are wrapped in a tagged payload object to allow 464 for the transaction to be dispatched to appropriate handling logic. 465 466 .. code-block:: protobuf 467 468 message SCPayload { 469 enum Action { 470 CREATE_AGENT = 1; 471 CREATE_RECORD = 2; 472 FINALIZE_RECORD = 3; 473 CREATE_RECORD_TYPE = 4; 474 UPDATE_PROPERTIES = 5; 475 CREATE_PROPOSAL = 6; 476 ANSWER_PROPOSAL = 7; 477 REVOKE_REPORTER = 8; 478 } 479 480 Action action = 1; 481 482 // Approximately when transaction was submitted, as a Unix UTC timestamp 483 uint64 timestamp = 2; 484 485 CreateAgentAction create_agent = 3; 486 CreateRecordAction create_record = 4; 487 FinalizeRecordAction finalize_record = 5; 488 CreateRecordTypeAction create_record_type = 6; 489 UpdatePropertiesAction update_properties = 7; 490 CreateProposalAction create_proposal = 8; 491 AnswerProposalAction answer_proposal = 9; 492 RevokeReporterAction revoke_reporter = 10; 493 } 494 495 496 Any transaction is invalid if its timestamp is greater than the 497 validator's system time. 498 499 500 Create Agent 501 ------------ 502 503 Create an Agent that can interact with Records. The ``signer_pubkey`` 504 in the transaction header is used as the Agent's public key. 505 506 .. code-block:: protobuf 507 508 message CreateAgentAction { 509 // The human-readable name of the Agent, not necessarily unique 510 string name = 1; 511 } 512 513 514 A CreateAgent transaction is invalid if there is already an Agent with 515 the signer's public key or if the name is the empty string. 516 517 518 .. _CreateRecord: 519 520 Create Record 521 ------------- 522 523 When an Agent creates a Record, the Record is initialized with that 524 Agent as both owner and custodian. Any Properties required of the 525 Record by its RecordType must have initial values provided. 526 527 .. code-block:: protobuf 528 529 message PropertyValue { 530 // The name of the property being set 531 string name = 1; 532 PropertySchema.DataType data_type = 2; 533 534 // The type-specific value to initialize or update a Property. Only 535 // one of these fields should be used, and it should match the type 536 // specified for this Property in the RecordType. 537 bytes bytes_value = 11; 538 string string_value = 12; 539 sint64 int_value = 13; 540 float float_value = 14; 541 Location location_value = 15; 542 } 543 544 message CreateRecordAction { 545 // The natural key of the Record 546 string record_id = 1; 547 548 // The name of the RecordType this Record belongs to 549 string record_type = 2; 550 551 repeated PropertyValue properties = 3; 552 } 553 554 555 A CreateRecord transaction is invalid if one of the following 556 conditions occurs: 557 558 - The signer is not registered as an Agent. 559 - The identifier is the empty string. 560 - The identifier belongs to an existing Record. 561 - A valid RecordType is not specified. 562 - Initial values are not provided for all of the Properties specified 563 as required by the RecordType. 564 - Initial values of the wrong type are provided. 565 566 567 Finalize Record 568 --------------- 569 570 A FinalizeRecord Transaction sets a Record’s ``final`` flag to true. A 571 finalized Record and its Properties cannot be updated. A Record cannot 572 be finalized except by its owner, and cannot be finalized if the owner 573 and custodian are not the same. 574 575 .. code-block:: protobuf 576 577 message FinalizeRecordAction { 578 string record_id = 1; 579 } 580 581 582 A FinalizeRecord transaction is invalid if one of the following 583 conditions occurs: 584 585 - The Record it targets does not exist. 586 - The Record it targets is already final. 587 - The signer is not both the Record's owner and custodian. 588 589 590 Create Record Type 591 ------------------ 592 593 The payload of the Transaction that creates RecordTypes is the same as 594 the RecordType object itself: it has a name and a list of Properties. 595 596 .. code-block:: protobuf 597 598 message CreateRecordTypeAction { 599 string name = 1; 600 601 repeated PropertySchema properties = 2; 602 } 603 604 605 A CreateRecordType transaction is invalid if one of the following 606 conditions occurs: 607 608 - The signer is not registered as an Agent. 609 - Its list of Properties is empty. 610 - The name of the RecordType is the empty string. 611 - A RecordType with its name already exists. 612 613 614 Update Properties 615 ----------------- 616 617 An UpdateProperties transaction contains a ``record_id`` and a list of 618 PropertyValues (see CreateRecord_ above). It can only be (validly) 619 sent by an Agent authorized to report on the Property. 620 621 .. code-block:: protobuf 622 623 message UpdatePropertiesAction { 624 // The natural key of the Record 625 string record_id = 1; 626 627 repeated PropertyValue properties = 2; 628 } 629 630 631 An UpdateProperties transaction is invalid if one of the following 632 conditions occurs: 633 634 - The Record does not exist. 635 - The Record is final. 636 - Its signer is not authorized to report on that Record. 637 - None of the provided PropertyValues match the types specified in the 638 Record's RecordType. 639 640 641 Create Proposal 642 --------------- 643 644 A CreateProposal transaction creates an open Proposal concerning some 645 Record from the signer to the receiving Agent. This Proposal can be 646 for transfer of ownership, transfer of custodianship, or authorization 647 to report. If it is a reporter authorization Proposal, a nonempty list 648 of Property names must be included. 649 650 .. code-block:: protobuf 651 652 message CreateProposalPayload { 653 enum Role { 654 OWNER = 1; 655 CUSTODIAN = 2; 656 REPORTER = 3; 657 } 658 659 string record_id = 1; 660 661 // The public key of the Agent to whom the Proposal is sent 662 // (must be different from the Agent sending the Proposal). 663 string receiving_agent = 3; 664 665 repeated string properties = 4; 666 667 Role role = 5; 668 } 669 670 671 A CreateProposal transaction is invalid if one of the following 672 conditions occurs: 673 674 - The signer is not the owner and the Proposal is for transfer of 675 ownership or reporter authorization. 676 - The signer is not the custodian and the Proposal is for transfer of 677 custodianship. 678 - The receiving Agent is not registered (the signer must be registered 679 as well, but this is implied by the previous two conditions). 680 - There is already an open Proposal for the Record and receiving Agent 681 for the specified role. 682 - The Record is final. 683 - The Proposal is for reporter authorization and the list of Property 684 names is empty. 685 686 687 Answer Proposal 688 --------------- 689 690 An Agent who is the receiving Agent for a Proposal for some Record can 691 accept or reject that Proposal, marking the Proposal's status as 692 ``accepted`` or ``rejected``. The Proposal's ``issuing_agent`` cannot 693 accept or reject it, but can cancel it. This will mark the Proposal's 694 status as ``canceled`` rather than ``rejected``. 695 696 .. code-block:: protobuf 697 698 message AnswerProposalPayload { 699 enum Role { 700 OWNER = 1; 701 CUSTODIAN = 2; 702 REPORTER = 3; 703 } 704 705 enum Response { 706 ACCEPT = 1; 707 REJECT = 2; 708 CANCEL = 3; 709 } 710 711 string record_id = 1; 712 string receiving_agent = 2; 713 Role role = 3; 714 Response response = 4; 715 } 716 717 718 Proposals can conflict, in the sense that a Record's owner might have 719 opened ownership transfer Proposals with several Agents at once. These 720 Proposals will not be closed if one of them is accepted. Instead, an 721 ``accept`` answer will check to verify that the issuing Agent is still 722 the owner or custodian of the Record. 723 724 An AnswerProposal transaction is invalid if one of the following 725 conditions occurs: 726 727 - There is no Proposal for that receiving agent, record, and role. 728 - The signer is not the receiving or issuing Agent of the Proposal. 729 - The signer is the receiving Agent and answers ``cancel``. 730 - The signer is the issuing Agent and answers anything other than 731 ``cancel``. 732 - The response is ``accept``, but the issuing Agent is no longer the 733 owner or custodian (as appropriate to the role) of the Record. 734 735 736 Revoke Reporter 737 --------------- 738 739 The owner of a Record can send a RevokeReporter transaction to remove 740 a reporter's authorization to report on one or more Properties for 741 that Record. This creates a Proposal which is immediately closed and 742 marked as accepted. 743 744 .. code-block:: protobuf 745 746 message RevokeReporterPayload { 747 string record_id = 1; 748 string reporter_id = 2; 749 750 // the Properties for which the reporter's authorization is revoked 751 repeated string properties = 3; 752 } 753 754 A RevokeReporter transaction is invalid if one of the following 755 conditions occurs: 756 757 - The Record does not exist. 758 - The Record is final. 759 - The signer is not the Record's owner. 760 - The reporter whose authorization is to be revoked is not an 761 authorized reporter for the Record.