codenotary / immudb

immudb - immutable database based on zero trust, SQL/Key-Value/Document model, tamperproof, data change history
https://immudb.io
Other
8.54k stars 341 forks source link

Provide code snippets for example usage of immudb #194

Closed vchain-us-mgmt closed 4 years ago

vchain-us-mgmt commented 4 years ago

Code example from immutest

padurean commented 4 years ago

@vchain-us-mgmt Here is a runnable Go code which exemplifies most of the operations supported by immudb (and it's Go API). @mmeloni Please review it, especially the consistency check. Thanks in advance.

package main

import (
    "context"
    "fmt"
    "os"
    "path/filepath"
    "time"

    immuapi "github.com/codenotary/immudb/pkg/api"
    immuschema "github.com/codenotary/immudb/pkg/api/schema"
    immuclient "github.com/codenotary/immudb/pkg/client"
    immulogger "github.com/codenotary/immudb/pkg/logger"
    immuserver "github.com/codenotary/immudb/pkg/server"
    immustore "github.com/codenotary/immudb/pkg/store"
)

func main() {
    //===> 1. Start a new server
    fmt.Println("1. Start immudb server ...")

    const logfile = "immuserver.log"
    flogger, file, err :=
        immulogger.NewFileLogger("immuserver ", logfile)
    if err != nil {
        exit(err)
    }
    defer func() {
        if err = file.Close(); err != nil {
            exit(err)
        }
    }()
    serverOptions := immuserver.DefaultOptions().WithLogfile(logfile)
    server := immuserver.DefaultServer().WithOptions(serverOptions).WithLogger(flogger)
    go func() {
        if err := server.Start(); err != nil {
            exit(err)
        }
    }()
    defer func() {
        err := server.Stop()
        // NOTE: this cleanup must NOT be done in a real-world scenario!
        cleanup(server.Options.Dir, server.Options.Logfile)
        if err != nil {
            exit(err)
        }
    }()
    // wait for server to start
    time.Sleep(100 * time.Millisecond)

    //===> 2. Connect a new client
    fmt.Println("2. Connect immudb client ...")

    client, err := immuclient.NewImmuClient(immuclient.DefaultOptions())
    if err != nil {
        exit(err)
    }

    ctx := context.Background()

    //===> 3. Write entries
    fmt.Println("3. Write entries ...")

    //------> Set
    key1, value1 := []byte("client:Ms. Noelia Jaskolski"), []byte("Visa 1514284849020756 09/21")
    index, err := client.Set(ctx, key1, value1)
    if err != nil {
        exit(err)
    }
    fmt.Println("   Set - add entry:")
    printItem(key1, value1, index)

    //------> SafeSet
    key2, value2 := []byte("client:Mr. Archibald Beatty"), []byte("Visa 6679499384784022 11/23")
    verifiedIndex, err := client.SafeSet(ctx, key2, value2)
    if err != nil {
        exit(err)
    }
    fmt.Println("   SafeSet - add and verify entry:")
    printItem(key2, value2, verifiedIndex)

    key3, value3 := []byte("client:Ms. Maci Schuppe"), []byte("MasterCard 2232703813463070 12/19")
    verifiedIndex, err = client.SafeSet(ctx, key3, value3)
    if err != nil {
        exit(err)
    }
    fmt.Println("   SafeSet - add and verify entry:")
    printItem(key3, value3, verifiedIndex)

    value3 = []byte("MasterCard 8069498678459876 10/22")
    verifiedIndex, err = client.SafeSet(ctx, key3, value3)
    if err != nil {
        exit(err)
    }
    fmt.Println("   SafeSet - update and verify entry:")
    printItem(key3, value3, verifiedIndex)

    //------> SafeReference
    key3Ref := append([]byte("reference:"), key3...)
    verifiedIndex, err = client.SafeReference(ctx, key3Ref, key3)
    if err != nil {
        exit(err)
    }
    fmt.Println("   SafeReference - add and verify a reference key to an existing entry:")
    printItem(key3Ref, value3, verifiedIndex)

    //------> SafeZAdd
    fmt.Println("   SafeZAdd - add and verify scores for existing keys to a new or existing sorted set:")
    set1 := []byte("SetOfClientsThatAreWomen")
    key1Score := 1.
    verifiedIndex, err = client.SafeZAdd(ctx, set1, key1Score, key1)
    if err != nil {
        exit(err)
    }
    printSetItem(set1, key1, key1Score, verifiedIndex)
    key3Score := 3.
    verifiedIndex, err = client.SafeZAdd(ctx, set1, key3Score, key3)
    if err != nil {
        exit(err)
    }
    fmt.Println("   ------")
    printSetItem(set1, key3, key3Score, verifiedIndex)

    //===> 4. Read entries
    fmt.Println("4. Read entries ...")

    //------> Get
    item, err := client.Get(ctx, key1)
    if err != nil {
        exit(err)
    }
    fmt.Println("   Get - fetch entry:")
    printItem(nil, nil, item)

    //------> SafeGet
    verifiedItem, err := client.SafeGet(ctx, key2)
    if err != nil {
        exit(err)
    }
    fmt.Println("   SafeGet - fetch and verify entry:")
    printItem(nil, nil, verifiedItem)

    verifiedItem, err = client.SafeGet(ctx, key3Ref)
    if err != nil {
        exit(err)
    }
    fmt.Println("   SafeGet - fetch and verify entry by reference key:")
    printItem(nil, nil, verifiedItem)

    //------> ZScan
    // zscan             Iterate over a sorted set
    structuredItemList, err := client.ZScan(ctx, set1)
    if err != nil {
        exit(err)
    }
    fmt.Println("   ZScan - iterate over a sorted set:")
    for _, item := range structuredItemList.Items {
        printItem(nil, nil, item)
        fmt.Println("   ------")
    }

    //------> Scan
    prefix := []byte("client:Mr.")
    structuredItemList, err = client.Scan(ctx, prefix)
    if err != nil {
        exit(err)
    }
    fmt.Printf("   Scan - iterate over keys having the specified prefix (e.g. \"%s\"):\n", prefix)
    for _, item := range structuredItemList.Items {
        printItem(nil, nil, item)
        fmt.Println("   ------")
    }

    //------> Count
    prefix = []byte("client:Ms.")
    itemsCount, err := client.Count(ctx, prefix)
    if err != nil {
        exit(err)
    }
    fmt.Printf("   Count - count keys having the specified prefix (e.g. \"%s\"):\n", prefix)
    fmt.Printf("    count:      %d\n", itemsCount.Count)

    //===> 5. Prove consistency and inclusion
    fmt.Println("5. Prove consistency and inclusion ...")

    //------> Current tree root
    fmt.Println("   Current root - return the last merkle tree root and index stored locally")
    currentRoot, err := client.CurrentRoot(ctx)
    if err != nil {
        exit(err)
    }
    if currentRoot.Root == nil {
        fmt.Println("no root found: immudb is empty")
    }
    fmt.Printf("    index:      %d\n    hash:       %x\n", currentRoot.Index, currentRoot.Root)

    //------> Add a new entry after getting current root
    fmt.Println("   Add a new entry after getting current root:")
    key4, value4 := []byte("client:Mr. Valentin Padurean"), []byte("MasterCard 2232703813463070 01/24")
    verifiedIndex, err = client.SafeSet(ctx, key4, value4)
    if err != nil {
        exit(err)
    }
    fmt.Println("   SafeSet - add and verify an entry after getting the current root:")
    printItem(key4, value4, verifiedIndex)

    //------> Check consistency between the previous root and latest root
    fmt.Println("   Consistency - check consistency between the previous root and latest root:")
    proof, err := client.Consistency(ctx, currentRoot.Index)
    if err != nil {
        exit(err)
    }
    fmt.Printf("    verified:   %t\n    firstRoot:  %x at index: %d\n   secondRoot: %x at index: %d\n",
        proof.Verify(immuschema.Root{Index: currentRoot.Index, Root: currentRoot.Root}),
        proof.FirstRoot,
        proof.First,
        proof.SecondRoot,
        proof.Second)

    //------> Check inclusion
    fmt.Println("   Inclusion - check if specified index is included in the current tree:")
    structuredItem, err := client.Get(ctx, key2)
    if err != nil {
        exit(err)
    }
    inclusionProof, err := client.Inclusion(ctx, structuredItem.Index)
    if err != nil {
        exit(err)
    }
    hash, err := structuredItem.Hash()
    fmt.Printf("    verified:   %t\n    hash:       %x at index: %d\n   root:       %x at index: %d\n",
        inclusionProof.Verify(structuredItem.Index, hash),
        inclusionProof.Leaf,
        inclusionProof.Index,
        inclusionProof.Root,
        inclusionProof.At)

    fmt.Println("\nDONE. ¯\\_(ツ)_/¯")
}

func exit(err error) {
    _, _ = fmt.Fprintln(os.Stderr, err)
    os.Exit(1)
}

func cleanup(dbDir string, logfile string) {
    // remove db
    os.RemoveAll(dbDir)
    // remove log file
    os.Remove(logfile)
    // remove root
    files, err := filepath.Glob("./\\.root*")
    if err == nil {
        for _, f := range files {
            os.Remove(f)
        }
    }
}

func printItem(key []byte, value []byte, message interface{}) {
    var index uint64
    ts := uint64(time.Now().Unix())
    var verified, isVerified bool
    var hash []byte
    switch m := message.(type) {
    case *immuschema.Index:
        index = m.Index
        dig := immuapi.Digest(index, key, value)
        hash = dig[:]
    case *immuclient.VerifiedIndex:
        index = m.Index
        dig := immuapi.Digest(index, key, value)
        hash = dig[:]
        verified = m.Verified
        isVerified = true
    case *immuschema.Item:
        key = m.Key
        value = m.Value
        index = m.Index
        hash = m.Hash()
    case *immuschema.StructuredItem:
        key = m.Key
        value = m.Value.Payload
        ts = m.Value.Timestamp
        index = m.Index
        hash, _ = m.Hash()
    case *immuclient.VerifiedItem:
        key = m.Key
        value = m.Value
        index = m.Index
        ts = m.Time
        verified = m.Verified
        isVerified = true
        me, _ := immuschema.Merge(value, ts)
        dig := immuapi.Digest(index, key, me)
        hash = dig[:]

    }
    if !isVerified {
        fmt.Printf("    index:      %d\n    key:        %s\n    value:      %s\n    hash:       %x\n    time:       %s\n",
            index,
            key,
            value,
            hash,
            time.Unix(int64(ts), 0))
        return
    }
    fmt.Printf("    index:      %d\n    key:        %s\n    value:      %s\n    hash:       %x\n    time:       %s\n    verified:   %t\n",
        index,
        key,
        value,
        hash,
        time.Unix(int64(ts), 0),
        verified)
}

func printSetItem(set []byte, rkey []byte, score float64, message interface{}) {
    var index uint64
    var verified, isVerified bool
    switch m := message.(type) {
    case *immuschema.Index:
        index = m.Index
    case *immuclient.VerifiedIndex:
        index = m.Index
        verified = m.Verified
        isVerified = true
    }
    key, err := immustore.SetKey(rkey, set, score)
    if err != nil {
        fmt.Print(err.Error())
    }
    if !isVerified {
        fmt.Printf("    index:      %d\n    set:        %s\n    key:        %s\n    score:      %f\n    value:      %s\n    hash:       %x\n",
            index,
            set,
            key,
            score,
            rkey,
            immuapi.Digest(index, key, rkey))
        return
    }
    fmt.Printf("    index:      %d\n    set:        %s\n    key:        %s\n    score:      %f\n    value:      %s\n    hash:       %x\n    verified:   %t\n",
        index,
        set,
        key,
        score,
        rkey,
        immuapi.Digest(index, key, rkey),
        verified)
}
padurean commented 4 years ago

Here's a simplified version: image