-
-
Notifications
You must be signed in to change notification settings - Fork 11
/
brfc.go
148 lines (119 loc) · 4.29 KB
/
brfc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package paymail
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
)
// BRFCSpec is a full BRFC specification document
//
// See more: http://bsvalias.org/01-brfc-specifications.html
type BRFCSpec struct {
Alias string `json:"alias,omitempty"` // Alias is used in the list of capabilities
Author string `json:"author"` // Free-form, could include a name, alias, paymail address, GitHub/social media handle, etc.
ID string `json:"id"` // Public BRFC ID
Supersedes string `json:"supersedes,omitempty"` // A BRFC ID (or list of IDs) that this document supersedes
Title string `json:"title"` // Title of the brfc
URL string `json:"url,omitempty"` // Public URL to view the specification
Version string `json:"version"` // No set format; could be a sequence number, publication date, or any other scheme
Valid bool `json:"valid"` // Validated the ID -> (title,author,version)
}
// LoadBRFCs will load the known "default" specifications into structs from JSON
//
// additionSpecifications is appended to the default specs
// BRFCKnownSpecifications is a local constant of JSON to preload known BRFC ids
func LoadBRFCs(additionalSpecifications string) ([]*BRFCSpec, error) {
// Load the default specs
specs := make([]*BRFCSpec, 0)
if err := json.Unmarshal(
[]byte(BRFCKnownSpecifications), &specs,
); err != nil {
// This error case should never occur since the JSON is hardcoded, but good practice anyway
return nil, err
}
// No additional specs to process
if len(additionalSpecifications) == 0 {
return specs, nil
}
// Process the additional specifications
var tempSpecs []*BRFCSpec
if err := json.Unmarshal(
[]byte(additionalSpecifications), &tempSpecs,
); err != nil {
return specs, err
}
// Add the specs to the existing specifications
for _, spec := range tempSpecs {
// Validate the spec before adding
if valid, id, err := spec.Validate(); err != nil {
return nil, err
} else if !valid {
return nil, fmt.Errorf("brfc: [%s] is invalid - id returned: %s vs %s", spec.Title, id, spec.ID)
}
// Add to existing list
specs = append(specs, spec)
}
return specs, nil
}
// Generate will generate a new BRFC ID from the given specification
//
// See more: http://bsvalias.org/01-02-brfc-id-assignment.html
func (b *BRFCSpec) Generate() error {
// Validate the title (only required field)
if len(b.Title) == 0 {
b.ID = ""
return fmt.Errorf("invalid brfc title, length: 0")
}
// Start a new SHA256 hash
h := sha256.New()
// Append all values (trim leading & trailing whitespace)
_, _ = h.Write([]byte(strings.TrimSpace(b.Title) + strings.TrimSpace(b.Author) + strings.TrimSpace(b.Version)))
// Start the double SHA256
h2 := sha256.New()
// Write the first SHA256 result
_, _ = h2.Write(h.Sum(nil))
// Create the final double SHA256
doubleHash := h2.Sum(nil)
// Reverse the order
for i, j := 0, len(doubleHash)-1; i < j; i, j = i+1, j-1 {
doubleHash[i], doubleHash[j] = doubleHash[j], doubleHash[i]
}
// Hex encode the value
hexDoubleHash := make([]byte, hex.EncodedLen(len(doubleHash)))
hex.Encode(hexDoubleHash, doubleHash)
// Check that the ID length is valid
// this error case was never hit as long as title is len() > 0
/*
if len(hexDoubleHash) < 12 {
b.ID = ""
return fmt.Errorf("failed to generate a valid id, length was %d", len(hexDoubleHash))
}
*/
// Extract the ID and set (first 12 characters)
b.ID = string(hexDoubleHash[:12])
return nil
}
// Validate will check if the BRFC is valid or not (and set b.Valid)
//
// Returns the ID that was generated to compare against the existing id
// Returns valid bool for convenience, but also sets b.Valid = true
func (b *BRFCSpec) Validate() (valid bool, id string, err error) {
// Copy and generate (copying ensures that running Generate() will not override the existing ID)
tempBRFC := new(BRFCSpec)
*tempBRFC = *b
// Start by invalidating the BRFC
b.Valid = false
// Run the generate method to return an ID
if err = tempBRFC.Generate(); err != nil {
return
}
// Set the ID generated (for external comparison etc.)
id = tempBRFC.ID
// Test if the ID generated matches what was set previously
if tempBRFC.ID == b.ID {
valid = true
b.Valid = valid
}
return
}