github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/adkgo-GENESIS/contracts/AGSClaim.sol (about) 1 // SPDX-License-Identifier: GPL-3.0 2 pragma solidity >0.8.4; 3 4 interface ADKTokenInterface2 { 5 function AZ9balanceOf(string memory _adkAddr) external view returns (uint256 balance); 6 function meshTransaction(string memory _meshaddr, int256 _value) external; 7 function AZ9_TO_ADDR (string memory adkString) external pure returns(address); 8 } 9 10 // Contract which allows claiming of native ADK. 11 12 contract AGSClaim { 13 14 constructor( 15 address _genesis_account, 16 address _ADKTokenContract 17 ) { 18 ADKTokenAddress = _ADKTokenContract; // The ADK Token Contract 19 adkgo_genesis_address = _genesis_account; 20 ClientProofOfWorkRequirement = 15; // PoW effort required (must be multiple of 3) 21 } 22 23 address public ADKTokenAddress; // Holds the address of the ADK Genesis Contract managing balances 24 address public adkgo_genesis_address; // Contract genesis address 25 uint256 public ClientProofOfWorkRequirement; 26 27 mapping(address => uint256) public claimable; 28 mapping(string => uint256) public claimableAZ9; // for information only 29 30 uint256 public InitialAGSAmount; 31 32 event EventClaimed(address indexed addr, address _to, uint256 claimedAmount); 33 event EventClaimedAZ9(string indexed addrAZ9, address _to, uint256 claimedAmount); 34 35 bool mutex_PostTransactions = false; // mutex to prevent reentry 36 function PostTransactions(string memory transactiondata) public mod_requireAZ9(transactiondata) returns(string memory) { 37 38 // Check mutex 39 require (!mutex_PostTransactions,"reentry prevented!"); 40 mutex_PostTransactions = true; // prevent reentry exploits 41 42 bytes memory b_trytes = bytes(transactiondata); // Load transaction AZ9 string into bytes 43 require(b_trytes.length % 2673 == 0 && b_trytes.length > 0 ,"Invalid transaction(s) length"); 44 uint16 cnt_transactions = uint16(b_trytes.length / 2673); // count number of included individual transactions 45 46 require(cnt_transactions==3, "must have 3 transactions"); 47 48 bytes memory all_essential_parts = new bytes(cnt_transactions*162); // each essential is 162 char long, all essentials make the bundle hash 49 50 string memory s_bundle = ""; // initialize 51 address payable ags_address; // initialize 52 string memory ags_address_AZ9; // initialize 53 54 address claim_address; // initialize 55 string memory claim_address_AZ9; // initialize 56 // process each transaction one by one 57 for (uint32 transaction_idx = 0; transaction_idx < cnt_transactions; transaction_idx++){ 58 59 TransactionInfoStruct memory tinfo; // will hold all key transaction data (also used to avoid stack errors due to too many variables) 60 tinfo.offset = transaction_idx * 2673; // offset of each transaction in the entire string 61 62 tinfo.data = substring(transactiondata,tinfo.offset , tinfo.offset + 2673); // extract the current transaction 63 tinfo.trans_hash = bytes(CurlHashOP(tinfo.data)); // Call the Hash Operation in order to get the transaction hash 64 65 // Validate PoW 66 // force the last X digits to be 0 (client POW); // 67 68 for ( uint checkIdx = 0; checkIdx < ClientProofOfWorkRequirement/3; checkIdx ++ ){ 69 uint byte_index = 80 - checkIdx; // 80,79,78,.. integer division to get byte position to check 70 // note we only check full bytes, not by trits. So difficulty can be e.g. ... , 9, 12, 15, 18, 21 ... 71 require(tinfo.trans_hash[byte_index] == 0x39, // 0x39 = "9", 72 "TRANSACTION POW NOT COMPLETED" 73 ); 74 } 75 76 // Transaction Hash Handling 77 tinfo.transactionSHA3 = keccak256(tinfo.trans_hash); // get keccak hash of transaction hash for indexing 78 79 // Bundle Hash: recurd current transaction essential parts for later bundle hash generation 80 81 for (uint16 idx = 0; idx < 162; idx++){ // get essential parts for bunlde hash calculation 82 all_essential_parts[(transaction_idx*162) + idx] = b_trytes[tinfo.offset + 2187 + idx]; 83 } 84 85 // Transaction Address Value Handling 86 87 tinfo.s_address = substring(tinfo.data,2187,2268); // get current transaction address 88 // check transaction indices 89 tinfo.b_bundle = subbytes(b_trytes,tinfo.offset+2349,tinfo.offset+2349+81); 90 91 if (transaction_idx==0){ //this is where to send the AGS to 92 s_bundle = string(tinfo.b_bundle); 93 require (compareStrings(substring(tinfo.data,0,16),"CLAIMTRANSACTION"),"not a claim transcation"); 94 } 95 else { 96 require(compareStrings(s_bundle, string(tinfo.b_bundle)),"bundle not consistent"); // bundle has to be the same across all transactions 97 } 98 99 // SIGNATURE VALIDATIONS /////////////////////// 100 // Check signature 101 102 if (transaction_idx==0) { 103 //first transactions indicates where to send the claimed AGS to 104 ags_address = payable(ADKTokenInterface2(ADKTokenAddress).AZ9_TO_ADDR(tinfo.s_address)); 105 ags_address_AZ9 = tinfo.s_address; 106 } 107 108 if (transaction_idx==1) { 109 // this is the claiming address 110 claim_address = ADKTokenInterface2(ADKTokenAddress).AZ9_TO_ADDR(tinfo.s_address); 111 claim_address_AZ9 = substring(transactiondata,tinfo.offset+2673+2187,tinfo.offset+2673+2268); 112 113 require (compareStrings(tinfo.s_address, claim_address_AZ9),"2nd signature has invalid address"); // there must be at least one more transaction 114 tinfo.sigA = substring(transactiondata,2673,2673+2187); 115 tinfo.sigB = substring(transactiondata,2*2673,2*2673+2187); 116 117 // Validate Signature 118 require( CurlValidateSignature(tinfo.s_address, 119 concat(tinfo.sigA,tinfo.sigB), 120 s_bundle), 121 "INVALID SIGNATURE"); 122 } 123 124 } // END LOOP THROUGH ALL TRANSACTIONS 125 126 // compute and check BUNDLE HASH: calculated bundle hash must match the actual bundle trytes stored in each transaction 127 128 string memory s_hash = CurlHashOP(string(all_essential_parts)); 129 require (compareStrings(s_bundle,s_hash),"CALCULATED BUNDLE DIFFERS"); 130 131 // if we are here, the bundle itself is valid. // we can now transfer the AGS 132 uint256 claimableAmount = claimable[claim_address]; 133 claimable[claim_address] = 0; 134 claimableAZ9[claim_address_AZ9] = 0; 135 136 require(claimableAmount>0,"Nothing to claim"); 137 ags_address.transfer(claimableAmount); 138 emit EventClaimed(claim_address, ags_address, claimableAmount); 139 emit EventClaimedAZ9(claim_address_AZ9, ags_address, claimableAmount); 140 141 mutex_PostTransactions = false; // end reentry check 142 return s_hash; // bundle hash 143 } 144 // 145 146 // Transaction Struct, mainly used to avoid stack issues 147 struct TransactionInfoStruct { 148 string s_address; 149 string data; // transaction data 150 string sigA; 151 string sigB; 152 bytes b_bundle; 153 uint32 offset; 154 bytes32 transactionSHA3; // KECCAK hash of transactionSHA3 155 bytes trans_hash; // CURL hash of transaction 156 } 157 158 159 // Allows for initial load of claimable balances while in Genesis Mode 160 function ADM_setClaimableAmount (string memory _AZ9addr, uint256 _claimableAmount) public onlyGenesis { 161 require ((bytes(_AZ9addr)).length==81,"invalid address"); 162 address addr = (ADKTokenInterface2(ADKTokenAddress).AZ9_TO_ADDR(_AZ9addr)); 163 claimable[addr] = _claimableAmount; 164 claimableAZ9[_AZ9addr] = _claimableAmount; 165 InitialAGSAmount += _claimableAmount; 166 } 167 // Allows for initial load of Balances while in Genesis Mode/ bulk mode for speedup 168 function ADM_setClaimableAmountBulk (string memory _addresses, 169 uint256 _value1, 170 uint256 _value2, 171 uint256 _value3, 172 uint256 _value4, 173 uint256 _value5, 174 uint256 _value6, 175 uint256 _value7, 176 uint256 _value8, 177 uint256 _value9, 178 uint256 _value10 179 ) public onlyGenesis { 180 uint32 pos = 0; 181 require (bytes(_addresses).length == 81 * 10 , "String must contain 10 addresses without checksum for bulk processing"); 182 ADM_setClaimableAmount(substring(_addresses,pos,pos+81), _value1); pos+= 81; 183 ADM_setClaimableAmount(substring(_addresses,pos,pos+81), _value2); pos+= 81; 184 ADM_setClaimableAmount(substring(_addresses,pos,pos+81), _value3); pos+= 81; 185 ADM_setClaimableAmount(substring(_addresses,pos,pos+81), _value4); pos+= 81; 186 ADM_setClaimableAmount(substring(_addresses,pos,pos+81), _value5); pos+= 81; 187 ADM_setClaimableAmount(substring(_addresses,pos,pos+81), _value6); pos+= 81; 188 ADM_setClaimableAmount(substring(_addresses,pos,pos+81), _value7); pos+= 81; 189 ADM_setClaimableAmount(substring(_addresses,pos,pos+81), _value8); pos+= 81; 190 ADM_setClaimableAmount(substring(_addresses,pos,pos+81), _value9); pos+= 81; 191 ADM_setClaimableAmount(substring(_addresses,pos,pos+81), _value10); 192 } 193 194 195 // Allows for upgrade of the ADK Genesis Address 196 // and will be used to force-lock the contract by setting to 0x00000000[..] so no further updates are possible 197 function ADM_SetGenesisAddress (address _genesisAddress) public onlyGenesis { 198 adkgo_genesis_address = _genesisAddress; 199 } 200 201 function recoverAGS(uint256 _bal) public onlyGenesis { 202 payable(msg.sender).transfer(_bal); 203 } 204 205 function getAGSBalance() public view returns (uint256) { 206 return address(this).balance; 207 } 208 209 210 // this is used to fund the Airdrop initially 211 fallback() external payable {} 212 213 // Checks if two strings are identical 214 function compareStrings(string memory a, string memory b) internal pure returns (bool) { 215 return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b)))); 216 } 217 218 // CurlHashOP - calculates a CURL hash for a given Tryte string 219 function CurlHashOP(string memory str) public pure returns (string memory){ 220 221 bytes memory pdata = bytes(string(abi.encodePacked("{CURL}", "HASH", bytes1(0x00), bytes1(0x00), str))); 222 bytes32 r1 = keccak256(pdata); 223 pdata[10] = 0x20; // next 32 chars 224 bytes32 r2 = keccak256(pdata); 225 pdata[10] = 0x40; // next 32 chars 226 bytes32 r3 = keccak256(pdata); 227 228 bytes memory ret = new bytes(81); 229 230 for (uint i = 0; i< 32; i++){ // 0-64 231 ret[i] = r1[i]; 232 ret[32+i] = r2[i]; 233 } 234 for (uint i = 0; i < 17; i++){ // 64-81 235 ret[64+i] = r3[i]; 236 } 237 238 return string(ret); 239 } 240 241 // Calls the ADK (gadk) Signature Validation Routine via the overloaded keccak function 242 243 function CurlValidateSignature(string memory addr, string memory signature, string memory bundle) internal pure returns (bool){ 244 245 bytes memory pdata = bytes(string(abi.encodePacked("{CURL}", "VALSIG", addr,signature,bundle))); 246 bytes32 result = keccak256(pdata); 247 248 return uint(result)==0; // must be 0, only then the signature is valid 249 } 250 251 // substring helper function 252 function substring(string memory str, uint32 startIndex, uint32 endIndex) internal pure returns (string memory ) { 253 bytes memory strBytes = bytes(str); 254 bytes memory result = new bytes(endIndex-startIndex); 255 for(uint32 i = startIndex; i < endIndex; i++) { 256 result[i-startIndex] = strBytes[i]; 257 } 258 return string(result); 259 } 260 261 // subbytes helper function 262 function subbytes(bytes memory strBytes, uint startIndex, uint endIndex) internal pure returns (bytes memory ) { 263 bytes memory result = new bytes(endIndex-startIndex); 264 for(uint i = startIndex; i < endIndex; i++) { 265 result[i-startIndex] = strBytes[i]; 266 } 267 return result; 268 } 269 270 // concat (strings) helper function 271 function concat(string memory a, string memory b) internal pure returns (string memory) { 272 return string(abi.encodePacked(a, b,"","","")); 273 } 274 275 // 276 // Modifiers ///////////////////////////////////////////////// 277 // 278 279 modifier mod_requireAZ9 (string memory _adk_string) { 280 bytes memory adkBytes = bytes (_adk_string); 281 require(adkBytes.length >= 1 ); 282 283 bool valid = true; 284 for (uint i = 0; i < adkBytes.length; i++) { 285 if ( 286 ! ( 287 uint8(adkBytes[i]) == 57 //9 288 || (uint8(adkBytes[i]) >= 65 && uint8(adkBytes[i]) <= 90) //A-Z 289 ) 290 ) valid = false; 291 } 292 require (valid, "INVALID TRYTES"); 293 _; 294 } 295 296 297 // MODIFIERS 298 299 modifier onlyGenesis { 300 require(msg.sender == adkgo_genesis_address, "NOT AUTHORIZED"); 301 _; 302 } 303 304 305 306 }