2024-01-03 13:36:23 +00:00
|
|
|
package encoding
|
2024-01-03 12:18:19 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/hex"
|
2024-01-04 09:19:24 +00:00
|
|
|
"encoding/json"
|
2024-01-03 12:18:19 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"git.lumeweb.com/LumeWeb/libs5-go/internal/bases"
|
|
|
|
"git.lumeweb.com/LumeWeb/libs5-go/types"
|
|
|
|
"git.lumeweb.com/LumeWeb/libs5-go/utils"
|
2024-01-04 12:44:09 +00:00
|
|
|
"github.com/vmihailenco/msgpack/v5"
|
2024-01-03 12:18:19 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
errEmptyBytes = errors.New("empty bytes")
|
|
|
|
errInvalidInputType = errors.New("invalid input type for bytes")
|
|
|
|
)
|
|
|
|
|
|
|
|
type CID struct {
|
2024-01-03 13:36:23 +00:00
|
|
|
Multibase
|
2024-01-03 12:18:19 +00:00
|
|
|
Type types.CIDType
|
2024-01-03 13:36:23 +00:00
|
|
|
Hash Multihash
|
2024-01-10 16:00:01 +00:00
|
|
|
Size uint64
|
2024-01-03 12:18:19 +00:00
|
|
|
}
|
|
|
|
|
2024-01-04 09:19:24 +00:00
|
|
|
var _ json.Marshaler = (*CID)(nil)
|
2024-01-04 13:19:26 +00:00
|
|
|
var _ json.Unmarshaler = (*CID)(nil)
|
2024-01-04 12:44:09 +00:00
|
|
|
var _ msgpack.CustomEncoder = (*CID)(nil)
|
|
|
|
var _ msgpack.CustomDecoder = (*CID)(nil)
|
2024-01-04 09:19:24 +00:00
|
|
|
|
2024-01-10 16:00:01 +00:00
|
|
|
func NewCID(Type types.CIDType, Hash Multihash, Size uint64) *CID {
|
2024-01-03 12:18:19 +00:00
|
|
|
c := &CID{
|
|
|
|
Type: Type,
|
|
|
|
Hash: Hash,
|
|
|
|
Size: Size,
|
|
|
|
}
|
2024-01-03 13:36:23 +00:00
|
|
|
m := NewMultibase(c)
|
2024-01-03 12:18:19 +00:00
|
|
|
c.Multibase = m
|
|
|
|
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cid *CID) getPrefixBytes() []byte {
|
|
|
|
return []byte{byte(cid.Type)}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cid *CID) ToBytes() []byte {
|
|
|
|
if cid.Type == types.CIDTypeBridge {
|
2024-01-06 12:11:51 +00:00
|
|
|
return cid.Hash.fullBytes
|
2024-01-03 12:18:19 +00:00
|
|
|
} else if cid.Type == types.CIDTypeRaw {
|
|
|
|
sizeBytes := utils.EncodeEndian(cid.Size, 8)
|
|
|
|
|
|
|
|
for len(sizeBytes) > 0 && sizeBytes[len(sizeBytes)-1] == 0 {
|
|
|
|
sizeBytes = sizeBytes[:len(sizeBytes)-1]
|
|
|
|
}
|
|
|
|
if len(sizeBytes) == 0 {
|
|
|
|
sizeBytes = []byte{0}
|
|
|
|
}
|
|
|
|
|
2024-01-06 12:11:51 +00:00
|
|
|
return utils.ConcatBytes(cid.getPrefixBytes(), cid.Hash.fullBytes, sizeBytes)
|
2024-01-03 12:18:19 +00:00
|
|
|
}
|
|
|
|
|
2024-01-06 12:11:51 +00:00
|
|
|
return utils.ConcatBytes(cid.getPrefixBytes(), cid.Hash.fullBytes)
|
2024-01-03 12:18:19 +00:00
|
|
|
}
|
|
|
|
|
2024-01-09 13:23:35 +00:00
|
|
|
func CIDFromString(cid string) (*CID, error) {
|
2024-01-03 13:36:23 +00:00
|
|
|
decodedBytes, err := MultibaseDecodeString(cid)
|
2024-01-03 12:18:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
cidInstance, err := initCID(decodedBytes)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return cidInstance, nil
|
|
|
|
}
|
|
|
|
|
2024-01-03 13:36:23 +00:00
|
|
|
func CIDFromRegistry(bytes []byte) (*CID, error) {
|
2024-01-03 12:18:19 +00:00
|
|
|
if len(bytes) == 0 {
|
|
|
|
return nil, errEmptyBytes
|
|
|
|
}
|
|
|
|
|
|
|
|
registryType := types.RegistryType(bytes[0])
|
|
|
|
if _, exists := types.RegistryTypeMap[registryType]; !exists {
|
|
|
|
return nil, fmt.Errorf("invalid registry type %d", bytes[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes = bytes[1:]
|
|
|
|
|
|
|
|
cidInstance, err := initCID(bytes)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return cidInstance, nil
|
|
|
|
}
|
|
|
|
|
2024-01-03 13:36:23 +00:00
|
|
|
func CIDFromBytes(bytes []byte) (*CID, error) {
|
2024-01-03 12:18:19 +00:00
|
|
|
return initCID(bytes)
|
|
|
|
}
|
|
|
|
|
2024-01-10 16:00:01 +00:00
|
|
|
func CIDFromHash(bytes interface{}, size uint64, cidType types.CIDType) (*CID, error) {
|
2024-01-03 12:18:19 +00:00
|
|
|
var (
|
|
|
|
byteSlice []byte
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
switch v := bytes.(type) {
|
|
|
|
case string:
|
|
|
|
byteSlice, err = hex.DecodeString(v)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
case []byte:
|
|
|
|
byteSlice = v
|
|
|
|
default:
|
|
|
|
return nil, errInvalidInputType
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, exists := types.CIDTypeMap[cidType]; !exists {
|
|
|
|
return nil, fmt.Errorf("invalid hash type %d", cidType)
|
|
|
|
}
|
|
|
|
|
2024-01-03 13:36:23 +00:00
|
|
|
return NewCID(cidType, *NewMultihash(byteSlice), size), nil
|
2024-01-03 12:18:19 +00:00
|
|
|
}
|
|
|
|
|
2024-01-03 13:36:23 +00:00
|
|
|
func CIDVerify(bytes interface{}) bool {
|
2024-01-03 12:18:19 +00:00
|
|
|
var (
|
|
|
|
byteSlice []byte
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
switch v := bytes.(type) {
|
|
|
|
case string:
|
2024-01-03 13:36:23 +00:00
|
|
|
byteSlice, err = MultibaseDecodeString(v) // Assuming MultibaseDecodeString function is defined
|
2024-01-03 12:18:19 +00:00
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
case []byte:
|
|
|
|
byteSlice = v
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = initCID(byteSlice)
|
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
|
2024-01-10 16:00:01 +00:00
|
|
|
func (cid *CID) CopyWith(newType int, newSize uint64) (*CID, error) {
|
2024-01-03 12:18:19 +00:00
|
|
|
if newType == 0 {
|
|
|
|
newType = int(cid.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, exists := types.CIDTypeMap[types.CIDType(newType)]; !exists {
|
|
|
|
return nil, fmt.Errorf("invalid cid type %d", newType)
|
|
|
|
}
|
|
|
|
|
2024-01-03 13:36:23 +00:00
|
|
|
return NewCID(types.CIDType(newType), cid.Hash, newSize), nil
|
2024-01-03 12:18:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (cid *CID) ToRegistryEntry() []byte {
|
|
|
|
registryType := types.RegistryTypeCID
|
|
|
|
cidBytes := cid.ToBytes()
|
|
|
|
return utils.ConcatBytes([]byte{byte(registryType)}, cidBytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cid *CID) ToRegistryCID() ([]byte, error) {
|
|
|
|
registryCIDType := types.CIDTypeResolver
|
|
|
|
copiedCID, err := cid.CopyWith(int(registryCIDType), cid.Size)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return copiedCID.ToBytes(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cid *CID) ToString() (string, error) {
|
|
|
|
if cid.Type == types.CIDTypeBridge {
|
|
|
|
return cid.Hash.ToString()
|
|
|
|
}
|
|
|
|
|
|
|
|
return bases.ToBase58BTC(cid.ToBytes())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cid *CID) Equals(other *CID) bool {
|
|
|
|
return bytes.Equal(cid.ToBytes(), other.ToBytes())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cid *CID) HashCode() int {
|
|
|
|
fullBytes := cid.ToBytes()
|
|
|
|
if len(fullBytes) < 4 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
return int(fullBytes[0]) +
|
|
|
|
int(fullBytes[1])<<8 +
|
|
|
|
int(fullBytes[2])<<16 +
|
|
|
|
int(fullBytes[3])<<24
|
|
|
|
}
|
|
|
|
|
2024-01-04 13:19:26 +00:00
|
|
|
func (b CID) MarshalJSON() ([]byte, error) {
|
|
|
|
url, err := b.ToBase64Url()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return json.Marshal(url)
|
2024-01-04 09:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (cid *CID) UnmarshalJSON(data []byte) error {
|
2024-01-04 14:51:35 +00:00
|
|
|
decData, err := UnmarshalBase64UrlJSON(data)
|
2024-01-04 13:19:26 +00:00
|
|
|
|
|
|
|
if err != nil {
|
2024-01-04 09:19:24 +00:00
|
|
|
return err
|
|
|
|
}
|
2024-01-04 13:19:26 +00:00
|
|
|
|
|
|
|
decodedCid, err := CIDFromBytes(decData)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
*cid = *decodedCid
|
2024-01-04 09:19:24 +00:00
|
|
|
return nil
|
|
|
|
}
|
2024-01-04 13:19:26 +00:00
|
|
|
|
2024-01-04 12:44:09 +00:00
|
|
|
func (cid CID) EncodeMsgpack(enc *msgpack.Encoder) error {
|
|
|
|
return enc.EncodeBytes(cid.ToBytes())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cid *CID) DecodeMsgpack(dec *msgpack.Decoder) error {
|
|
|
|
return decodeMsgpackCID(cid, dec)
|
|
|
|
}
|
|
|
|
|
2024-01-03 13:36:23 +00:00
|
|
|
func CIDFromRegistryPublicKey(pubkey interface{}) (*CID, error) {
|
|
|
|
return CIDFromHash(pubkey, 0, types.CIDTypeResolver)
|
2024-01-03 12:18:19 +00:00
|
|
|
}
|
|
|
|
|
2024-01-04 12:44:09 +00:00
|
|
|
func decodeMsgpackCID(cid interface{}, dec *msgpack.Decoder) error {
|
|
|
|
byt, err := dec.DecodeBytes()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch v := cid.(type) {
|
|
|
|
case *CID:
|
|
|
|
cidInstance, err := CIDFromBytes(byt)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
*v = *cidInstance
|
|
|
|
case *EncryptedCID:
|
|
|
|
cidInstance, err := EncryptedCIDFromBytes(byt)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
*v = *cidInstance
|
|
|
|
default:
|
|
|
|
return errors.New("Unsupported type")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-01-03 12:18:19 +00:00
|
|
|
func initCID(bytes []byte) (*CID, error) {
|
|
|
|
if len(bytes) == 0 {
|
|
|
|
return nil, errEmptyBytes
|
|
|
|
}
|
|
|
|
|
|
|
|
cidType := types.CIDType(bytes[0])
|
|
|
|
if cidType == types.CIDTypeBridge {
|
2024-01-03 13:36:23 +00:00
|
|
|
hash := NewMultihash(bytes[1:35])
|
|
|
|
return NewCID(cidType, *hash, 0), nil
|
2024-01-03 12:18:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
hashBytes := bytes[1:34]
|
2024-01-03 13:36:23 +00:00
|
|
|
hash := NewMultihash(hashBytes)
|
2024-01-03 12:18:19 +00:00
|
|
|
|
2024-01-10 16:00:01 +00:00
|
|
|
var size uint64
|
2024-01-03 12:18:19 +00:00
|
|
|
if len(bytes) > 34 {
|
|
|
|
sizeBytes := bytes[34:]
|
|
|
|
sizeValue := utils.DecodeEndian(sizeBytes)
|
|
|
|
size = sizeValue
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, exists := types.CIDTypeMap[cidType]; !exists {
|
|
|
|
return nil, fmt.Errorf("invalid cid type %d", cidType)
|
|
|
|
}
|
|
|
|
|
2024-01-03 13:36:23 +00:00
|
|
|
return NewCID(cidType, *hash, size), nil
|
2024-01-03 12:18:19 +00:00
|
|
|
}
|