about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorian Klink <flokli@flokli.de>2023-10-05T15·15+0300
committerflokli <flokli@flokli.de>2023-10-05T18·42+0000
commit600815c16846de7428f554313657e4f3f934fc0a (patch)
tree9477688fcbb01a1cb23c99daddd673ced4e557d4
parent618853d6b0a2e3a78a3faef79d8ff295665411e1 (diff)
feat(tvix/store/protos): add Validate() method to pathinfo.go r/6712
This is very similar to the Rust counterpart.

Change-Id: I40d51aaac3fcf7f52e5896587e561bc2377f6269
Reviewed-on: https://cl.tvl.fyi/c/depot/+/9549
Reviewed-by: tazjin <tazjin@tvl.su>
Autosubmit: flokli <flokli@flokli.de>
Reviewed-by: Connor Brewster <cbrewster@hey.com>
Tested-by: BuildkiteCI
-rw-r--r--tvix/default.nix4
-rw-r--r--tvix/store/protos/go.mod7
-rw-r--r--tvix/store/protos/go.sum14
-rw-r--r--tvix/store/protos/pathinfo.go93
4 files changed, 107 insertions, 11 deletions
diff --git a/tvix/default.nix b/tvix/default.nix
index d8027213f6..94f50b29ba 100644
--- a/tvix/default.nix
+++ b/tvix/default.nix
@@ -112,14 +112,14 @@ in
   castore-protos-go = pkgs.buildGoModule {
     name = "castore-golang";
     src = depot.third_party.gitignoreSource ./store/protos;
-    vendorHash = "sha256-zRbMwhZvcDop8qDd0OrHeDJ+lVPKxwjKmlFbj8Ypy9g=";
+    vendorHash = "sha256-o7moXRxhKxCpsds96sSsHHafKJf2AWhFMu/YdSu+FM4=";
   };
 
   # Builds and tests the code in store/protos.
   store-protos-go = pkgs.buildGoModule {
     name = "store-golang";
     src = depot.third_party.gitignoreSource ./store/protos;
-    vendorHash = "sha256-zRbMwhZvcDop8qDd0OrHeDJ+lVPKxwjKmlFbj8Ypy9g=";
+    vendorHash = "sha256-o7moXRxhKxCpsds96sSsHHafKJf2AWhFMu/YdSu+FM4=";
   };
 
   # Build the Rust documentation for publishing on docs.tvix.dev.
diff --git a/tvix/store/protos/go.mod b/tvix/store/protos/go.mod
index 23241f9154..3038b2a6c5 100644
--- a/tvix/store/protos/go.mod
+++ b/tvix/store/protos/go.mod
@@ -4,6 +4,7 @@ go 1.19
 
 require (
 	code.tvl.fyi/tvix/castore/protos v0.0.0-20230922125121-72355662d742
+	github.com/nix-community/go-nix v0.0.0-20231005143722-b0f8b73c06df
 	google.golang.org/grpc v1.51.0
 	google.golang.org/protobuf v1.28.1
 )
@@ -11,9 +12,9 @@ require (
 require (
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/klauspost/cpuid/v2 v2.0.9 // indirect
-	golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
-	golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
-	golang.org/x/text v0.4.0 // indirect
+	golang.org/x/net v0.7.0 // indirect
+	golang.org/x/sys v0.5.0 // indirect
+	golang.org/x/text v0.7.0 // indirect
 	google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
 	lukechampine.com/blake3 v1.1.7 // indirect
 )
diff --git a/tvix/store/protos/go.sum b/tvix/store/protos/go.sum
index c2fac11d28..e1840f8d15 100644
--- a/tvix/store/protos/go.sum
+++ b/tvix/store/protos/go.sum
@@ -28,6 +28,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
 github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/nix-community/go-nix v0.0.0-20231005143722-b0f8b73c06df h1:n4I26uXUST5vmdsDWPo9ikK57il4htQyhnsLWoHYFmY=
+github.com/nix-community/go-nix v0.0.0-20231005143722-b0f8b73c06df/go.mod h1:hHM9UK2zOCjvmiLgeaW4LVbOW/vBaRWFJGzfi31/slQ=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
@@ -40,19 +42,19 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
diff --git a/tvix/store/protos/pathinfo.go b/tvix/store/protos/pathinfo.go
new file mode 100644
index 0000000000..6607b5c879
--- /dev/null
+++ b/tvix/store/protos/pathinfo.go
@@ -0,0 +1,93 @@
+package storev1
+
+import (
+	"bytes"
+	"encoding/base64"
+	"fmt"
+
+	"github.com/nix-community/go-nix/pkg/storepath"
+)
+
+// Validate performs some checks on the PathInfo struct, returning either the
+// StorePath of the root node, or an error.
+func (p *PathInfo) Validate() (*storepath.StorePath, error) {
+	// ensure References has the right number of bytes.
+	for i, reference := range p.GetReferences() {
+		if len(reference) != storepath.PathHashSize {
+			return nil, fmt.Errorf("invalid length of digest at position %d, expected %d, got %d", i, storepath.PathHashSize, len(reference))
+		}
+	}
+
+	// If there's a Narinfo field populated..
+	if narInfo := p.GetNarinfo(); narInfo != nil {
+		// ensure the number of references matches len(References).
+		if len(narInfo.GetReferenceNames()) != len(p.GetReferences()) {
+			return nil, fmt.Errorf("inconsistent number of references: %d (references) vs %d (narinfo)", len(narInfo.GetReferenceNames()), len(p.GetReferences()))
+		}
+
+		// for each ReferenceName…
+		for i, referenceName := range narInfo.GetReferenceNames() {
+			// ensure it parses to a store path
+			storePath, err := storepath.FromString(referenceName)
+			if err != nil {
+				return nil, fmt.Errorf("invalid ReferenceName at position %d: %w", i, err)
+			}
+
+			// ensure the digest matches the one at References[i]
+			if !bytes.Equal(p.GetReferences()[i], storePath.Digest) {
+				return nil, fmt.Errorf(
+					"digest in ReferenceName at position %d does not match digest in PathInfo, expected %s, got %s",
+					i,
+					base64.StdEncoding.EncodeToString(p.GetReferences()[i]),
+					base64.StdEncoding.EncodeToString(storePath.Digest),
+				)
+			}
+		}
+	}
+
+	// ensure there is a (root) node present
+	rootNode := p.GetNode()
+	if rootNode == nil {
+		return nil, fmt.Errorf("root node must be set")
+	}
+
+	// ensure it properly parses to a store path, and in case it refers to a digest, ensure it has the right length.
+	if node := rootNode.GetDirectory(); node != nil {
+		if len(node.Digest) != 32 {
+			return nil, fmt.Errorf("invalid digest size for %s, expected %d, got %d", node.Name, 32, len(node.Digest))
+		}
+
+		storePath, err := storepath.FromString(string(node.GetName()))
+
+		if err != nil {
+			return nil, fmt.Errorf("unable to parse %s as StorePath: %w", node.Name, err)
+		}
+
+		return storePath, nil
+
+	} else if node := rootNode.GetFile(); node != nil {
+		if len(node.Digest) != 32 {
+			return nil, fmt.Errorf("invalid digest size for %s, expected %d, got %d", node.Name, 32, len(node.Digest))
+		}
+
+		storePath, err := storepath.FromString(string(node.GetName()))
+		if err != nil {
+			return nil, fmt.Errorf("unable to parse %s as StorePath: %w", node.Name, err)
+		}
+
+		return storePath, nil
+
+	} else if node := rootNode.GetSymlink(); node != nil {
+		storePath, err := storepath.FromString(string(node.GetName()))
+
+		if err != nil {
+			return nil, fmt.Errorf("unable to parse %s as StorePath: %w", node.Name, err)
+		}
+
+		return storePath, nil
+
+	} else {
+		// this would only happen if we introduced a new type
+		panic("unreachable")
+	}
+}