code.vegaprotocol.io/vega@v0.79.0/libs/crypto/proof_of_work.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package crypto
    17  
    18  import (
    19  	"bytes"
    20  	"encoding/binary"
    21  	"errors"
    22  	"math"
    23  )
    24  
    25  const (
    26  	Sha3     = "sha3_24_rounds"
    27  	maxNonce = math.MaxInt64
    28  )
    29  
    30  var prefix = []byte("Vega_SPAM_PoW")
    31  
    32  // PoW calculates proof of work given block hash, transaction hash, target difficulty and a hash function.
    33  // returns the nonce, the hash and the error if any.
    34  func PoW(blockHash string, txID string, difficulty uint, hashFunction string) (uint64, []byte, error) {
    35  	var h []byte
    36  	var err error
    37  	nonce := uint64(0)
    38  
    39  	if difficulty > 256 {
    40  		return nonce, h, errors.New("invalid difficulty")
    41  	}
    42  
    43  	if len(txID) < 1 {
    44  		return nonce, h, errors.New("transaction ID cannot be empty")
    45  	}
    46  
    47  	if len(blockHash) != 64 {
    48  		return nonce, h, errors.New("incorrect block hash")
    49  	}
    50  
    51  	for nonce < maxNonce {
    52  		data := prepareData(blockHash, txID, nonce)
    53  		h, err = hash(data, hashFunction)
    54  		if err != nil {
    55  			return nonce, h, err
    56  		}
    57  
    58  		if CountZeros(h) >= byte(difficulty) {
    59  			break
    60  		} else {
    61  			nonce++
    62  		}
    63  	}
    64  
    65  	return nonce, h[:], nil
    66  }
    67  
    68  // Verify checks that the hash with the given nonce results in the target difficulty.
    69  func Verify(blockHash string, tid string, nonce uint64, hashFuncion string, difficulty uint) (bool, byte) {
    70  	if difficulty > 256 {
    71  		return false, 0
    72  	}
    73  
    74  	if len(tid) < 1 {
    75  		return false, 0
    76  	}
    77  
    78  	if len(blockHash) != 64 {
    79  		return false, 0
    80  	}
    81  
    82  	data := prepareData(blockHash, tid, nonce)
    83  	h, err := hash(data, hashFuncion)
    84  	if err != nil {
    85  		return false, 0
    86  	}
    87  	hDiff := CountZeros(h)
    88  	return hDiff >= byte(difficulty), hDiff
    89  }
    90  
    91  func CountZeros(d []byte) byte {
    92  	var ret byte
    93  	for _, x := range d {
    94  		if x == 0 {
    95  			ret += 8
    96  		} else {
    97  			if x&128 != 0x00 {
    98  				break
    99  			}
   100  			if x&64 != 0x00 {
   101  				ret++
   102  				break
   103  			}
   104  			if x&32 != 0x00 {
   105  				ret += 2
   106  				break
   107  			}
   108  			if x&16 != 0x00 {
   109  				ret += 3
   110  				break
   111  			}
   112  			if x&8 != 0x00 {
   113  				ret += 4
   114  				break
   115  			}
   116  			if x&4 != 0x00 {
   117  				ret += 5
   118  				break
   119  			}
   120  			if x&2 != 0x00 {
   121  				ret += 6
   122  				break
   123  			}
   124  			if x&1 != 0x00 {
   125  				ret += 7
   126  			}
   127  			break
   128  		}
   129  	}
   130  	return ret
   131  }
   132  
   133  func prepareData(blockHash string, txID string, nonce uint64) []byte {
   134  	return bytes.Join(
   135  		[][]byte{
   136  			prefix,
   137  			[]byte(blockHash),
   138  			[]byte(txID),
   139  			IntToHex(nonce),
   140  		},
   141  		[]byte{},
   142  	)
   143  }
   144  
   145  func hash(data []byte, hashFunction string) ([]byte, error) {
   146  	if hashFunction == Sha3 {
   147  		return Hash(data), nil
   148  	}
   149  	return nil, errors.New("unknown hash function")
   150  }
   151  
   152  func IntToHex(num uint64) []byte {
   153  	bs := make([]byte, 8)
   154  	binary.BigEndian.PutUint64(bs, num)
   155  	return bs
   156  }