event-msg

Simple, embedded news post vehicle for EVM smart contracts
Log | Files | Refs

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 }