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  }