Trillian Map Mode
- 4 minutes read - 808 wordsChatting with one of Google’s Trillian team, I was encouraged to explore Trillian’s Map Mode. The following is the result of some spelunking through this unfamiliar cave. I can’t provide any guarantee that this usage is correct or sufficient.
Here’s the repo: https://github.com/DazWilkin/go-trillian-map
I’ve written about Trillian Log Mode elsewhere.
I uncovered use of Trillian Map Mode through Trillian’s integration tests. I’m unclear on the distinction between TrillianMapClient and TrillianMapWriteClient but the latter served most of my needs.
Trillian includes sample deployment examples for Docker Compose and Kubernetes. This repo includes both.
First, we need a Map.
GRPC="50051"
MAPID=$(\
go run github.com/google/trillian/cmd/createtree \
--admin_server=:${GRPC} \
--tree_type=MAP \
--hash_strategy=CONIKS_SHA512_256) && echo ${MAPID}
NOTE we must override defaults values for
--tree_typeand--hash_strategyto create a Map
The example is trivial but demonstrates writing and getting data from the Map.
One (of the) aspect(s) that I don’t grok with Maps is the concept of revisions. I suspect (and need to determine whether) I can try getting leaves from any revision (see Issue #2)
The example uses a trivial map of key-value pairs that are used to create Leaves for the Map.
var Examples = map[string]string{
"Freddie": "Border Collie",
"Noisette": "Australian Shepherd",
"Artie": "Weimeraner",
"Louie": "Formosan Mountain Dog",
"Luna": "Yorkiepoo",
"Bertie": "Cockapoo",
"Mr Magoo": "Scottish Terrier",
}
The Add section batches the leaves into an []*trillian.MapLeaf that is added in a single WriteLeaves request (using a single revision).
MapLeaf comprise an Index and a LeafValue. Both are []byte and Index is a hash value in the Trillian uses. In the following, key and value correspond to entries in the above map:
hasher := sha256.New()
hasher.Write([]byte(key))
index := hasher.Sum(nil)
leaf := &trillian.MapLeaf{
Index: index,
LeafValue: []byte(value),
}
An evolution of the code that would mainly be for intellectual curiosity, would be to replace the simple string values with a protobuf or MessagePack marshaled value (see Issues #3)
Then to write the leaves, simply:
func (c *Client) Add(ctx context.Context, leaves []*trillian.MapLeaf, revision int64) error {
log.Print("[Client:Add] Entered")
rqst := &trillian.WriteMapLeavesRequest{
MapId: c.mapID,
Leaves: leaves,
ExpectRevision: revision,
}
resp, err := c.client.WriteLeaves(ctx, rqst)
if err != nil {
return err
}
log.Printf("[Client:Add] %+v", resp)
return nil
}
The Get section gets each leaf in turn for the revision. As before, we hash the key value to create the index and then create the request with a singular index. We iterate over the leaves in the response and print them:
indexes := [][]byte{
index,
}
leaves, err := client.Get(ctx, indexes, rev)
if err != nil {
log.Fatal(err)
}
for i, leaf := range leaves {
log.Printf("[main:get] Leaf #%02d: %s", i, toString(leaf))
}
The leaf retrieval uses Trillian’s GetMapLeavesByRevisionRequest:
func (c *Client) Get(ctx context.Context, indexes [][]byte, revision int64) ([]*trillian.MapLeaf, error) {
log.Print("[Client:Get] Entered")
rqst := &trillian.GetMapLeavesByRevisionRequest{
MapId: c.mapID,
Index: indexes,
Revision: revision,
}
resp, err := c.client.GetLeavesByRevision(ctx, rqst)
if err != nil {
return nil, err
}
log.Printf("[Client:Get] %+v", resp)
return resp.GetLeaves(), nil
}
Running the server against the Kubernetes deployment involves determining the code’s (Kubernetes) Service endpoint:
ENDPOINT=$(\
kubectl get service/map-server \
--namespace=trillian \
--output=jsonpath="{.spec.clusterIP}:{.spec.ports[?(@.name==\"grpc\")].port}"\
) && echo ${ENDPOINT}
Then invoking the binary:
go run ./cmd/server \
--tmap_endpoint=${ENDPOINT} \
--tmap_id=${MAPID} \
--tmap_rev=1
Yielding:
[main:add] Bertie
[main:add] Bertie: 2eed8f9879977565d3037e6363b505ff2ca35a2227083de91d198ebfcde76b3d
[main:add] Leaf #00: Index: 2eed8f9879977565d3037e6363b505ff2ca35a2227083de91d198ebfcde76b3d, Value: Cockapoo
[main:add] Mr Magoo
[main:add] Mr Magoo: cec7fa35beed1c12b44f1bab965fc47ebe8c590510a7eb774d10b440f68c8355
[main:add] Leaf #01: Index: cec7fa35beed1c12b44f1bab965fc47ebe8c590510a7eb774d10b440f68c8355, Value: Scottish Terrier
[main:add] Freddie
[main:add] Freddie: b146ae2a1fb1d772cbe9c16fd9ae30eb11ebef7c948829167bb04fae541aa5f5
[main:add] Leaf #02: Index: b146ae2a1fb1d772cbe9c16fd9ae30eb11ebef7c948829167bb04fae541aa5f5, Value: Border Collie
[main:add] Noisette
[main:add] Noisette: 12bb631807ea32865a0611e72a65e2b33ec9ad8b9600e9db4d29837481e45b38
[main:add] Leaf #03: Index: 12bb631807ea32865a0611e72a65e2b33ec9ad8b9600e9db4d29837481e45b38, Value: Australian Shepherd
[main:add] Artie
[main:add] Artie: d69c866405c582529c4c74fe474575a62c0db4fa8e5321f62fb197cbb7bc8fe7
[main:add] Leaf #04: Index: d69c866405c582529c4c74fe474575a62c0db4fa8e5321f62fb197cbb7bc8fe7, Value: Weimeraner
[main:add] Louie
[main:add] Louie: 7435d5755367c07fd592e3210fe0db424a9278b255faab470c96324809d1a773
[main:add] Leaf #05: Index: 7435d5755367c07fd592e3210fe0db424a9278b255faab470c96324809d1a773, Value: Formosan Mountain Dog
[main:add] Luna
[main:add] Luna: 9d77a24d0f4c91a2e968ca607e49dd3b15d5489f66a5bc81fa72803b32443419
[main:add] Leaf #06: Index: 9d77a24d0f4c91a2e968ca607e49dd3b15d5489f66a5bc81fa72803b32443419, Value: Yorkiepoo
[main:add] Add'ing
[Client:Add] Entered
[Client:Add] revision:7
[main:get] Freddie
[main:get] Freddie: b146ae2a1fb1d772cbe9c16fd9ae30eb11ebef7c948829167bb04fae541aa5f5
[main:get] Get'ing Freddie
[Client:Get] Entered
[Client:Get] leaves:{index:"\xb1F\xae*\x1f\xb1\xd7r\xcb\xe9\xc1oٮ0\xeb\x11\xeb\xef|\x94\x88)\x16{\xb0O\xaeT\x1a\xa5\xf5" leaf_value:"Border Collie"}
[main:get] Leaf #00: Index: b146ae2a1fb1d772cbe9c16fd9ae30eb11ebef7c948829167bb04fae541aa5f5, Value: Border Collie
[main:get] Sleeping 1 second
[main:get] Noisette
[main:get] Noisette: 12bb631807ea32865a0611e72a65e2b33ec9ad8b9600e9db4d29837481e45b38
[main:get] Get'ing Noisette
[Client:Get] Entered
[Client:Get] leaves:{index:"\x12\xbbc\x18\x07\xea2\x86Z\x06\x11\xe7*e\xe2\xb3>ɭ\x8b\x96\x00\xe9\xdbM)\x83t\x81\xe4[8" leaf_value:"Australian Shepherd"}
[main:get] Leaf #00: Index: 12bb631807ea32865a0611e72a65e2b33ec9ad8b9600e9db4d29837481e45b38, Value: Australian Shepherd
[main:get] Sleeping 1 second
[main:get] Artie
[main:get] Artie: d69c866405c582529c4c74fe474575a62c0db4fa8e5321f62fb197cbb7bc8fe7
[main:get] Get'ing Artie
[Client:Get] Entered
[Client:Get] leaves:{index:"֜\x86d\x05łR\x9cLt\xfeGEu\xa6,\r\xb4\xfa\x8eS!\xf6/\xb1\x97˷\xbc\x8f\xe7" leaf_value:"Weimeraner"}
[main:get] Leaf #00: Index: d69c866405c582529c4c74fe474575a62c0db4fa8e5321f62fb197cbb7bc8fe7, Value: Weimeraner
[main:get] Sleeping 1 second
[main:get] Louie
[main:get] Louie: 7435d5755367c07fd592e3210fe0db424a9278b255faab470c96324809d1a773
[main:get] Get'ing Louie
[Client:Get] Entered
[Client:Get] leaves:{index:"t5\xd5uSg\xc0Ւ\xe3!\x0f\xe0\xdbBJ\x92x\xb2U\xfa\xabG\x0c\x962H\tѧs" leaf_value:"Formosan Mountain Dog"}
[main:get] Leaf #00: Index: 7435d5755367c07fd592e3210fe0db424a9278b255faab470c96324809d1a773, Value: Formosan Mountain Dog
[main:get] Sleeping 1 second
[main:get] Luna
[main:get] Luna: 9d77a24d0f4c91a2e968ca607e49dd3b15d5489f66a5bc81fa72803b32443419
[main:get] Get'ing Luna
[Client:Get] Entered
[Client:Get] leaves:{index:"\x9dw\xa2M\x0fL\x91\xa2\xe9h\xca`~I\xdd;\x15\xd5H\x9ff\xa5\xbc\x81\xfar\x80;2D4\x19" leaf_value:"Yorkiepoo"}
[main:get] Leaf #00: Index: 9d77a24d0f4c91a2e968ca607e49dd3b15d5489f66a5bc81fa72803b32443419, Value: Yorkiepoo
[main:get] Sleeping 1 second
[main:get] Bertie
[main:get] Bertie: 2eed8f9879977565d3037e6363b505ff2ca35a2227083de91d198ebfcde76b3d
[main:get] Get'ing Bertie
[Client:Get] Entered
[Client:Get] leaves:{index:".폘y\x97ue\xd3\x03~cc\xb5\x05\xff,\xa3Z\"'\x08=\xe9\x1d\x19\x8e\xbf\xcd\xe7k=" leaf_value:"Cockapoo"}
[main:get] Leaf #00: Index: 2eed8f9879977565d3037e6363b505ff2ca35a2227083de91d198ebfcde76b3d, Value: Cockapoo
[main:get] Sleeping 1 second
[main:get] Mr Magoo
[main:get] Mr Magoo: cec7fa35beed1c12b44f1bab965fc47ebe8c590510a7eb774d10b440f68c8355
[main:get] Get'ing Mr Magoo
[Client:Get] Entered
[Client:Get] leaves:{index:"\xce\xc7\xfa5\xbe\xed\x1c\x12\xb4O\x1b\xab\x96_\xc4~\xbe\x8cY\x05\x10\xa7\xebwM\x10\xb4@\xf6\x8c\x83U" leaf_value:"Scottish Terrier"}
[main:get] Leaf #00: Index: cec7fa35beed1c12b44f1bab965fc47ebe8c590510a7eb774d10b440f68c8355, Value: Scottish Terrier
[main:get] Sleeping 1 second
That’s all (for now)!