Msg.sol (6354B)
1 pragma solidity >=0.8.0; 2 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 5 contract EventMsg { 6 struct MultiHash { 7 uint8 l; 8 uint8 codecRLength; 9 uint8 prefixRLength; 10 bytes16 prefix; 11 bytes8 codec; 12 } 13 14 // Owner of contract 15 address public owner; 16 17 // All registered multicodecs 18 mapping (uint256 => MultiHash) public multiCodec; 19 20 // Implements Digest 21 uint256 public defaultDigestEncoding; 22 23 // Latest persisted message 24 bytes currentMsg; 25 26 // Editable base URI against which to look up token data by token id 27 bytes public baseURL; 28 29 // Implements Seal 30 uint256 public sealState; 31 uint8 constant CODECLIST_STATE = 1; 32 uint8 constant CODEC_STATE = 2; 33 // Implements Seal 34 uint256 constant public maxSealState = 3; 35 // Implements Seal 36 event SealStateChange(bool indexed _final, uint256 _sealState); 37 38 // Implements Msg 39 event Msg(bytes _multiHash); 40 41 constructor() { 42 owner = msg.sender; 43 addCodec(32, 0x12, "sha256"); 44 setMsgCodec(0x12); 45 currentMsg = new bytes(32); 46 baseURL = "http://localhost/"; 47 } 48 49 // Set the latest pesistent message on contract 50 function setMsg(bytes memory _digest) public { 51 MultiHash storage _hsh; 52 53 _hsh = multiCodec[defaultDigestEncoding]; 54 require(_digest.length == _hsh.l); 55 56 currentMsg = _digest; 57 emit Msg(getMsg()); 58 } 59 60 // Implements Msg 61 function getMsg() public view returns(bytes memory) { 62 return encodeDigest(currentMsg); 63 } 64 65 // Add a multicodec that can later be set as current codec 66 function addCodec(uint8 _length, uint64 _codecId, string memory _uriPrefix) public { 67 require(sealState & CODECLIST_STATE == 0, 'ERR_SEAL_CODECLIST'); 68 bytes memory prefixBytes; 69 70 prefixBytes = bytes(_uriPrefix); 71 require(prefixBytes.length <= 16, 'ERR_PREFIX_TOO_LONG'); 72 MultiHash memory _hsh; 73 uint8 c; 74 75 c = 7; 76 while (c >= 0) { 77 uint64 mask = uint64(0xff << (c * 8)); 78 if ((mask & _codecId) > 0) { 79 break; 80 } 81 c--; 82 } 83 _hsh.codecRLength = c + 1; 84 _hsh.codec = bytes8(_codecId << ((7 - c) * 8)); 85 _hsh.prefixRLength = uint8(prefixBytes.length); 86 _hsh.prefix = bytes16(prefixBytes); 87 _hsh.l = _length; 88 89 multiCodec[uint256(_codecId)] = _hsh; 90 } 91 92 // Set the current multicodec to use for multihash generation 93 function setMsgCodec(uint256 _codec) public { 94 require(sealState & CODECLIST_STATE == 0, 'ERR_SEAL_CODEC'); 95 MultiHash storage _hsh; 96 97 _hsh = multiCodec[_codec]; 98 require(_hsh.l > 0); 99 100 defaultDigestEncoding = _codec; 101 currentMsg = new bytes(_hsh.l); 102 103 emit Msg(getMsg()); 104 } 105 106 // Seal the given state 107 function seal(uint256 _state) public returns(uint256) { 108 require(_state < 16, 'ERR_INVALID_STATE'); 109 require(_state & sealState == 0, 'ERR_ALREADY_LOCKED'); 110 sealState |= _state; 111 emit SealStateChange(sealState == maxSealState, sealState); 112 return uint256(sealState); 113 } 114 115 // Generate a multihash from the given digest and current selected multicodec 116 function toMultiHash(bytes memory _digest, uint256 _codec) private view returns(bytes memory) { 117 MultiHash storage m; 118 bytes memory r; 119 120 m = multiCodec[_codec]; 121 r = new bytes(_digest.length + m.l + m.codecRLength); 122 123 uint256 i = 0; 124 for (i; i < m.codecRLength; i++) { 125 r[i] = m.codec[i]; 126 } 127 r[i] = bytes1(m.l); 128 i++; 129 for (uint256 j = 0; j < _digest.length; j++) { 130 r[i+j] = _digest[j]; 131 } 132 133 return r; 134 } 135 136 // Implements Digest 137 function encodeDigest(bytes memory _digest) public view returns(bytes memory) { 138 return toMultiHash(_digest, defaultDigestEncoding); 139 } 140 141 // Implements Digest 142 function encodeDigest(bytes memory _digest, uint256 _codec) public view returns(bytes memory) { 143 return toMultiHash(_digest, _codec); 144 } 145 146 // Implements Digest 147 function haveDigestEncoding(uint256 _codec) public view returns(bool) { 148 MultiHash memory m; 149 150 m = multiCodec[_codec]; 151 return m.l > 0; 152 } 153 154 // Generate a URI representing the digest and the string prefix representation // of the currently selected multicodec 155 // Implements Locator 156 function toURI(bytes memory _digest) public view returns(string memory) { 157 MultiHash storage m; 158 159 bytes memory codecString; 160 bytes memory digestHex; 161 uint256 l; 162 163 digestHex = toHex(_digest); 164 m = multiCodec[defaultDigestEncoding]; 165 l = m.prefixRLength; 166 codecString = new bytes(l + digestHex.length + 1); 167 for (uint256 i = 0; i < l; i++) { 168 codecString[i] = m.prefix[i]; 169 } 170 codecString[l] = 0x3a; 171 l++; 172 173 for (uint256 i = 0; i < digestHex.length; i++) { 174 codecString[l+i] = digestHex[i]; 175 } 176 return string(codecString); 177 178 } 179 180 // Allow mutable explicit url base 181 function setBaseURL(string memory _baseString) public { 182 bytes memory _base; 183 uint256 l; 184 require(msg.sender == owner); 185 186 _base = bytes(_baseString); 187 l = _base.length; 188 if (_base[l-1] != 0x2f) { 189 l++; 190 } 191 baseURL = new bytes(l); 192 for (uint256 i = 0; i < _base.length; i++) { 193 baseURL[i] = _base[i]; 194 } 195 if (l != _base.length) { 196 baseURL[_base.length] = "/"; 197 } 198 } 199 200 // Implements Locator 201 function toURL(bytes memory _digest) public view returns(string memory) { 202 bytes memory out; 203 bytes memory _hexDigest; 204 uint256 c; 205 206 _hexDigest = toHex(_digest); 207 208 c = baseURL.length; 209 out = new bytes(_hexDigest.length + c); 210 211 for (uint256 i = 0; i < c; i++) { 212 out[i] = baseURL[i]; 213 } 214 for (uint256 i = 0; i < _hexDigest.length; i++) { 215 out[c] = _hexDigest[i]; 216 c++; 217 } 218 return string(out); 219 } 220 221 // TODO: move to internal library method 222 // bytes to hex conversion 223 function toHex(bytes memory _data) public pure returns(bytes memory) { 224 bytes memory out; 225 uint8 t; 226 uint256 c; 227 228 out = new bytes(_data.length * 2); 229 c = 0; 230 for (uint256 i = 0; i < 32; i++) { 231 t = (uint8(_data[i]) & 0xf0) >> 4; 232 if (t < 10) { 233 out[c] = bytes1(t + 0x30); 234 } else { 235 out[c] = bytes1(t + 0x57); 236 } 237 t = uint8(_data[i]) & 0x0f; 238 if (t < 10) { 239 out[c+1] = bytes1(t + 0x30); 240 } else { 241 out[c+1] = bytes1(t + 0x57); 242 } 243 c += 2; 244 } 245 return out; 246 } 247 248 // Implements ERC165 249 function supportsInterface(bytes4 _interfaceID) public pure returns(bool) { 250 if (_interfaceID == 0x982ab05d) { // Digest 251 return true; 252 } 253 if (_interfaceID == 0x01ffc9a7) { // ERC165 254 return true; 255 } 256 if (_interfaceID == 0xa3002595) { // Msg 257 return true; 258 } 259 if (_interfaceID == 0xed75b333) { // Locator 260 return true; 261 } 262 if (_interfaceID == 0x0d7491f8) { // Seal 263 return true; 264 } 265 return false; 266 } 267 }