github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/types/unlockhash.go (about) 1 package types 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 8 "github.com/NebulousLabs/Sia/crypto" 9 ) 10 11 // unlockhash.go contains the unlockhash alias along with usability methods 12 // such as String and an implementation of sort.Interface. 13 14 const ( 15 // UnlockHashChecksumSize is the size of the checksum used to verify 16 // human-readable addresses. It is not a crypytographically secure 17 // checksum, it's merely intended to prevent typos. 6 is chosen because it 18 // brings the total size of the address to 38 bytes, leaving 2 bytes for 19 // potential version additions in the future. 20 UnlockHashChecksumSize = 6 21 ) 22 23 // An UnlockHash is a specially constructed hash of the UnlockConditions type. 24 // "Locked" values can be unlocked by providing the UnlockConditions that hash 25 // to a given UnlockHash. See SpendConditions.UnlockHash for details on how the 26 // UnlockHash is constructed. 27 type UnlockHash crypto.Hash 28 29 type UnlockHashSlice []UnlockHash 30 31 // MarshalJSON is implemented on the unlock hash to always produce a hex string 32 // upon marshalling. 33 func (uh UnlockHash) MarshalJSON() ([]byte, error) { 34 return json.Marshal(uh.String()) 35 } 36 37 // UnmarshalJSON is implemented on the unlock hash to recover an unlock hash 38 // that has been encoded to a hex string. 39 func (uh *UnlockHash) UnmarshalJSON(b []byte) error { 40 // Check the length of b. 41 if len(b) != crypto.HashSize*2+UnlockHashChecksumSize*2+2 && len(b) != crypto.HashSize*2+2 { 42 return ErrUnlockHashWrongLen 43 } 44 return uh.LoadString(string(b[1 : len(b)-1])) 45 } 46 47 // String returns the hex representation of the unlock hash as a string - this 48 // includes a checksum. 49 func (uh UnlockHash) String() string { 50 uhChecksum := crypto.HashObject(uh) 51 return fmt.Sprintf("%x%x", uh[:], uhChecksum[:UnlockHashChecksumSize]) 52 } 53 54 // LoadString loads a hex representation (including checksum) of an unlock hash 55 // into an unlock hash object. An error is returned if the string is invalid or 56 // fails the checksum. 57 func (uh *UnlockHash) LoadString(strUH string) error { 58 // Check the length of strUH. 59 if len(strUH) != crypto.HashSize*2+UnlockHashChecksumSize*2 && len(strUH) != crypto.HashSize*2 { 60 return ErrUnlockHashWrongLen 61 } 62 63 // Decode the unlock hash. 64 var byteUnlockHash []byte 65 var checksum []byte 66 _, err := fmt.Sscanf(strUH[:crypto.HashSize*2], "%x", &byteUnlockHash) 67 if err != nil { 68 return err 69 } 70 // Decode the checksum, if provided. 71 if len(strUH) == crypto.HashSize*2+UnlockHashChecksumSize*2 { 72 _, err = fmt.Sscanf(strUH[crypto.HashSize*2:], "%x", &checksum) 73 if err != nil { 74 return err 75 } 76 77 // Verify the checksum - leave uh as-is unless the checksum is valid. 78 expectedChecksum := crypto.HashBytes(byteUnlockHash) 79 if !bytes.Equal(expectedChecksum[:UnlockHashChecksumSize], checksum) { 80 return ErrInvalidUnlockHashChecksum 81 } 82 } 83 copy(uh[:], byteUnlockHash[:]) 84 85 return nil 86 } 87 88 // Len implements the Len method of sort.Interface. 89 func (uhs UnlockHashSlice) Len() int { 90 return len(uhs) 91 } 92 93 // Less implements the Less method of sort.Interface. 94 func (uhs UnlockHashSlice) Less(i, j int) bool { 95 return bytes.Compare(uhs[i][:], uhs[j][:]) < 0 96 } 97 98 // Swap implements the Swap method of sort.Interface. 99 func (uhs UnlockHashSlice) Swap(i, j int) { 100 uhs[i], uhs[j] = uhs[j], uhs[i] 101 }