pragma solidity ^0.4.22; /* Invoice contract Represents a single sales invoice issued from a vendor to a customer © 2019 RTA Written by Tom Hall, January 2019 */ contract invoice { // Contract variables address public vendor; // Address of vendor address public customer; // Address of customer string public singleEntry; // Single Entry accounting baby string public decryptionKey; // Key to decrypt IPFS hashes bool public matched; // Match status of invoice bool public auditOpinions; // Audit opinions // Access rights object struct accessRights { string role; // Access rights' user role bool grantedByVendor; // Source of access rights } // Audit opinion object struct auditOpinion { address auditor; // Auditor address string detailedOpinion; // Auditor opinion detail } // Event logging (ledger) event submissionEvent( bool vendor, // Submitting party uint rtaPoint, // RTA timestamp string encryptedIpfsHash, // IPFS hash of encrypted submission data bool status // Match status on submission ); // Invoice callers mapping => access rights mapping (address => mapping(accessRights => bool)) thirdParties; // Access control // Audit opinion mapping => mapping (bool => auditOpinion) opinion; // Opinion mapping // MATCHING FUNCTIONS /** @dev Constructor function * @param _encryptedIpfsHash Encrypted IPFS hash * @param _customer Customer Address * @param _decryptionKey Decryption Key for IPFS Hash */ function invoice(string _encryptedIpfsHash, address _customer, string _decryptionKey) public { /* Currently set up to receive decyption key generated by Crowd Machine We should use an oracle instead to assert non-inpudation of randomness Kaleido Chainlink will enable this - function to be called on construct */ // Set contract variables - permanent vendor = msg.sender; // Set vendor customer = _customer; // Set customer decryptionKey = _decryptionKey; // Set protected decryptionKey // Set contract variables - temporary singleEntry = _encryptedIpfsHash; // Set current vendorRecords matched = false; // Set match status // Save data to logs emit submissionEvent(true, now, _encryptedIpfsHash, false); } /** @dev Updates invoice values. Restricted to vendor and customer only * @param _encryptedIpfsHash User Submission */ function updateInvoice(string _encryptedIpfsHash) public isParty(msg.sender) { testMatch(_encryptedIpfsHash); if (msg.sender == vendor) { emit submissionEvent(matched, now, _encryptedIpfsHash, true); } else { emit submissionEvent(matched, now, _encryptedIpfsHash, false); } singleEntry = _encryptedIpfsHash; // Set latest encrypted IPFS Hash } /** @dev Matching function * @param _encryptedIpfsHash Value to test */ function testMatch(string _encryptedIpfsHash) private isParty(msg.sender) { if (_encryptedIpfsHash == singleEntry) { matched = true; } else { matched = false; } } /** @dev Returns latest IPFS Hash * @return singleEntry Latest IPFS Hash */ function getInvoice() public returns (string) { return singleEntry; } /** @dev Returns match status of invoice * @return matched Matched status */ function getStatus() public returns (bool) { return matched; } function getVendor() public returns (address) { return vendor; } function getCustomer() public returns (address) { return customer; } // FACTORING FUNCTIONS function sellInvoice(address _newOwner) public isVendor(msg.sender) { /* Calls _newOwner receiver to create new invoice contract New IPFS hash generated replacing vendor data with _newOwner data ... we want the customer to retain the same contract address... ??? */ } function collateraliseInvoice(address _creditor) isVendor(msg.sender) public { /* Registers a charge against the invoice that auto-executes transfer on failure of conditions */ } // AUDITING FUNCTIONS /* function provideAuditOpinion(bool _auditOpinion, string _detailedOpinion) public { auditOpinions(_auditOpinion).opinion.auditor = msg.sender; auditOpinions(_auditOpinion).opinion.detailedOpinion = _detailedOpinion; } */ // ACCESS CONTROLS /** @dev Returns key needed to decrypt IPFS hash of encrypted data * @return decryptionkey Decryption key */ // TO DO: decryption key access logging function getDecryptionKey() canAccess(msg.sender) public returns (string) { return decryptionKey; } /** @dev Grants access rights and sets role of specified user * @param _party Specified user address * @param _role Specified user access role */ // TO DO: access control logging function grantAccess(address _thirdParty, string _role) partyCanShareWith(_thirdParty) { if (msg.sender == vendor) { thirdParties[_thirdParty][_role][true] = true; } else { thirdParties[_thirdParty][_role][false] = true; } } /** @dev Revokes access rights of specified user * @param _party Specified user address */ // TO DO: access control logging function revokeAccessFor(address _thirdParty) partyCanShareWith(_thirdParty) { if (msg.sender == vendor) { thirdParties[_thirdParty][_role][true] = false; } else { thirdParties[_thirdParty][_role][false] = false; } } /** @dev Determines if user can update access rights of specified user * @param _party Specified user address */ modifier partyCanShareWith(address _thirdParty) { // If not vendor or customer then exit if (msg.sender != vendor || customer) { throw; } // Vendor controls shares with vendor's callers only if (msg.sender == vendor && thirdParties[_thirdParty].grantedByVendor == false) { throw; } // Customer controls shares with customer's callers only if (msg.sender == customer && thirdParties[_thirdParty].grantedByVendor == true) { throw; } } /** @dev Determines if caller is party to the invoice */ modifier isParty() { require(msg.sender == (vendor || customer)); } modifier isVendor() { require(msg.sender == vendor); } /** @dev Determines if caller can access function */ modifier canAccess() { require(msg.sender == (vendor || customer) || (thirdParties[msg.sender][][] == true)); } }
0.4.22