beyond-blockchain/bbclib-go

View on GitHub
relation.go

Summary

Maintainability
C
1 day
Test Coverage
/*
Copyright (c) 2018 Zettant Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package bbclib

import (
    "bytes"
    "encoding/binary"
    "errors"
    "fmt"
)

/*
BBcRelation definition

The BBcRelation holds the asset (by BBcAsset) and the relationship with the other transaction/asset (by BBcPointer).
Different from UTXO, state information or account-type information can be expressed by using this object.
If you want to include signature(s) according to the contents of BBcRelation object, BBcWitness should be included in the transaction object.

"AssetGroupID" distinguishes a type of asset, e.g., token-X, token-Y, Movie content, etc..
"Pointers" is a list of BBcPointers object. "Asset" is a BBcAsset object.

"IDLength" is not included in a packed data. It is for internal use only.
*/
type (
    BBcRelation struct {
        IdLengthConf *BBcIdConfig
        Version      uint32
        AssetGroupID []byte
        Pointers     []*BBcPointer
        Asset        *BBcAsset
        AssetRaw     *BBcAssetRaw
        AssetHash    *BBcAssetHash
    }
)

// Stringer outputs the content of the object
func (p *BBcRelation) Stringer() string {
    ret := fmt.Sprintf("  asset_group_id: %x\n", p.AssetGroupID)
    if p.Pointers != nil {
        ret += fmt.Sprintf("  Pointers[]: %d\n", len(p.Pointers))
        for i := range p.Pointers {
            ret += fmt.Sprintf("   [%d]\n", i)
            ret += p.Pointers[i].Stringer()
        }
    } else {
        ret += fmt.Sprintf("  Pointers[]: None\n")
    }
    if p.Asset != nil {
        ret += p.Asset.Stringer()
    } else {
        ret += fmt.Sprintf("  Asset: None\n")
    }
    if p.AssetRaw != nil {
        ret += p.AssetRaw.Stringer()
    } else {
        ret += fmt.Sprintf("  AssetRaw: None\n")
    }
    if p.AssetHash != nil {
        ret += p.AssetHash.Stringer()
    } else {
        ret += fmt.Sprintf("  AssetHash: None\n")
    }
    return ret
}

// Set ID length configuration
func (p *BBcRelation) SetIdLengthConf(conf * BBcIdConfig) {
    p.IdLengthConf = conf
}

// Set version of the transaction format
func (p *BBcRelation) SetVersion(version uint32) {
    p.Version = version
}

// SetAssetGroup sets asset_group_id in the BBcRelation object
func (p *BBcRelation) SetAssetGroup(assetGroupId *[]byte) *BBcRelation {
    p.AssetGroupID = make([]byte, p.IdLengthConf.AssetGroupIdLength)
    copy(p.AssetGroupID, (*assetGroupId)[:p.IdLengthConf.AssetGroupIdLength])
    return p
}

// Add sets essential information (assetGroupID and BBcAsset object) to the BBcRelation object
func (p *BBcRelation) CreateAsset(userId *[]byte, fileContent *[]byte, bodyContent interface{}) *BBcRelation {
    obj := BBcAsset{Version: p.Version}
    obj.SetIdLengthConf(p.IdLengthConf)
    obj.Add(userId)
    if fileContent != nil {
        obj.AddFile(fileContent)
    }
    if bodyContent != nil {
        obj.AddBody(bodyContent)
    }
    p.Asset = &obj
    return p
}

// Add sets essential information (assetGroupID and BBcAssetRaw object) to the BBcRelation object
func (p *BBcRelation) CreateAssetRaw(assetID *[]byte, bodyContent interface{}) *BBcRelation {
    obj := BBcAssetRaw{Version: p.Version}
    obj.SetIdLengthConf(p.IdLengthConf)
    if bodyContent != nil {
        obj.AddBody(assetID, bodyContent)
    }
    p.AssetRaw = &obj
    return p
}

// Add sets essential information (assetGroupID and BBcAssetHash object) to the BBcRelation object
func (p *BBcRelation) CreateAssetHash(assetId *[]byte) *BBcRelation {
    if p.AssetHash == nil {
        obj := BBcAssetHash{Version: p.Version}
        obj.SetIdLengthConf(p.IdLengthConf)
        p.AssetHash = &obj
    }
    p.AssetHash.AddAssetId(assetId)
    return p
}

// AddPointer sets the BBcPointer object in the object
func (p *BBcRelation) CreatePointer(transactionId, assetId *[]byte) *BBcRelation {
    obj := BBcPointer{}
    obj.SetIdLengthConf(p.IdLengthConf)
    obj.Add(transactionId, assetId)
    p.Pointers = append(p.Pointers, &obj)
    return p
}


// Pack returns the binary data of the BBcRelation object
func (p *BBcRelation) Pack() ([]byte, error) {
    if p.AssetGroupID == nil {
        return nil, errors.New("need asset_group_id in BBcRelation")
    }
    buf := new(bytes.Buffer)

    PutBigInt(buf, &p.AssetGroupID, p.IdLengthConf.AssetGroupIdLength)

    Put2byte(buf, uint16(len(p.Pointers)))
    for _, p := range p.Pointers {
        dat, er := p.Pack()
        if er != nil {
            return nil, er
        }
        Put2byte(buf, uint16(binary.Size(dat)))
        if err := binary.Write(buf, binary.LittleEndian, dat); err != nil {
            return nil, err
        }
    }
    if p.Asset != nil {
        ast, er := p.Asset.Pack()
        if er != nil {
            return nil, er
        }
        Put4byte(buf, uint32(binary.Size(ast)))
        if err := binary.Write(buf, binary.LittleEndian, ast); err != nil {
            return nil, err
        }
    } else {
        Put4byte(buf, 0)
    }

    if p.Version >= 2 {
        if p.AssetRaw != nil {
            ast, er := p.AssetRaw.Pack()
            if er != nil {
                return nil, er
            }
            Put4byte(buf, uint32(binary.Size(ast)))
            if err := binary.Write(buf, binary.LittleEndian, ast); err != nil {
                return nil, err
            }
        } else {
            Put4byte(buf, 0)
        }

        if p.AssetHash != nil {
            ast, er := p.AssetHash.Pack()
            if er != nil {
                return nil, er
            }
            Put4byte(buf, uint32(binary.Size(ast)))
            if err := binary.Write(buf, binary.LittleEndian, ast); err != nil {
                return nil, err
            }
        } else {
            Put4byte(buf, 0)
        }
    }
    return buf.Bytes(), nil
}

// Unpack the BBcRelation object to the binary data
func (p *BBcRelation) Unpack(dat *[]byte) error {
    if p.IdLengthConf == nil {
        p.IdLengthConf = &BBcIdConfig{}
    }

    var err error
    buf := bytes.NewBuffer(*dat)

    p.AssetGroupID, p.IdLengthConf.AssetGroupIdLength, err = GetBigInt(buf)
    if err != nil {
        return err
    }

    numPointers, err := Get2byte(buf)
    if err != nil {
        return err
    }
    for i := 0; i < int(numPointers); i++ {
        size, err2 := Get2byte(buf)
        if err2 != nil {
            return err2
        }
        ptr, _, _ := GetBytes(buf, int(size))
        pointer := BBcPointer{}
        pointer.Unpack(&ptr)
        p.Pointers = append(p.Pointers, &pointer)
    }

    assetSize, err := Get4byte(buf)
    if err != nil {
        return err
    }
    if assetSize > 0 {
        ast, _, err := GetBytes(buf, int(assetSize))
        if err != nil {
            return err
        }
        p.Asset = &BBcAsset{}
        p.Asset.Unpack(&ast)
        UpdateIdLengthConfig(p.IdLengthConf, p.Asset.IdLengthConf)
    }

    if p.Version >= 2 {
        assetSize, err := Get4byte(buf)
        if err != nil {
            return err
        }
        if assetSize > 0 {
            ast, _, err := GetBytes(buf, int(assetSize))
            if err != nil {
                return err
            }
            p.AssetRaw = &BBcAssetRaw{}
            p.AssetRaw.Unpack(&ast)
            UpdateIdLengthConfig(p.IdLengthConf, p.AssetRaw.IdLengthConf)
        }

        assetSize, err = Get4byte(buf)
        if err != nil {
            return err
        }
        if assetSize > 0 {
            ast, _, err := GetBytes(buf, int(assetSize))
            if err != nil {
                return err
            }
            p.AssetHash = &BBcAssetHash{}
            p.AssetHash.Unpack(&ast)
            UpdateIdLengthConfig(p.IdLengthConf, p.AssetHash.IdLengthConf)
        }
    }

    return nil
}