multihash.go 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. // Copyright 2018 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package multihash
  17. import (
  18. "bytes"
  19. "encoding/binary"
  20. "errors"
  21. "fmt"
  22. )
  23. const (
  24. defaultMultihashLength = 32
  25. defaultMultihashTypeCode = 0x1b
  26. )
  27. var (
  28. multihashTypeCode uint8
  29. MultihashLength = defaultMultihashLength
  30. )
  31. func init() {
  32. multihashTypeCode = defaultMultihashTypeCode
  33. MultihashLength = defaultMultihashLength
  34. }
  35. // check if valid swarm multihash
  36. func isSwarmMultihashType(code uint8) bool {
  37. return code == multihashTypeCode
  38. }
  39. // GetMultihashLength returns the digest length of the provided multihash
  40. // It will fail if the multihash is not a valid swarm mulithash
  41. func GetMultihashLength(data []byte) (int, int, error) {
  42. cursor := 0
  43. typ, c := binary.Uvarint(data)
  44. if c <= 0 {
  45. return 0, 0, errors.New("unreadable hashtype field")
  46. }
  47. if !isSwarmMultihashType(uint8(typ)) {
  48. return 0, 0, fmt.Errorf("hash code %x is not a swarm hashtype", typ)
  49. }
  50. cursor += c
  51. hashlength, c := binary.Uvarint(data[cursor:])
  52. if c <= 0 {
  53. return 0, 0, errors.New("unreadable length field")
  54. }
  55. cursor += c
  56. // we cheekily assume hashlength < maxint
  57. inthashlength := int(hashlength)
  58. if len(data[c:]) < inthashlength {
  59. return 0, 0, errors.New("length mismatch")
  60. }
  61. return inthashlength, cursor, nil
  62. }
  63. // FromMulithash returns the digest portion of the multihash
  64. // It will fail if the multihash is not a valid swarm multihash
  65. func FromMultihash(data []byte) ([]byte, error) {
  66. hashLength, _, err := GetMultihashLength(data)
  67. if err != nil {
  68. return nil, err
  69. }
  70. return data[len(data)-hashLength:], nil
  71. }
  72. // ToMulithash wraps the provided digest data with a swarm mulithash header
  73. func ToMultihash(hashData []byte) []byte {
  74. buf := bytes.NewBuffer(nil)
  75. b := make([]byte, 8)
  76. c := binary.PutUvarint(b, uint64(multihashTypeCode))
  77. buf.Write(b[:c])
  78. c = binary.PutUvarint(b, uint64(len(hashData)))
  79. buf.Write(b[:c])
  80. buf.Write(hashData)
  81. return buf.Bytes()
  82. }