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 }