diff --git a/blob/blob_test.go b/blob/blob_test.go index e5e430e..cf12948 100644 --- a/blob/blob_test.go +++ b/blob/blob_test.go @@ -18,13 +18,13 @@ func TestBlob_Reading(t *testing.T) { require.Equal(t, "foobar", bl.Name()) var expSector Sector - _, err = f.ReadAt(expSector[:], 65536) + _, err = f.ReadAt(expSector[:], SectorBytes) require.NoError(t, err) actSector, err := bl.ReadSector(1) require.NoError(t, err) require.Equal(t, expSector, actSector) actData := make([]byte, 10, 10) - _, err = bl.ReadAt(actData, 65536) + _, err = bl.ReadAt(actData, SectorBytes) require.NoError(t, err) require.EqualValues(t, expSector[:10], actData) } diff --git a/blob/consts.go b/blob/consts.go index 43e0668..33babbd 100644 --- a/blob/consts.go +++ b/blob/consts.go @@ -2,8 +2,8 @@ package blob const ( HeaderLen = 1 + 1 + 63 + 8 + 32 + 65 - SectorLen = 4096 - SectorCount = 256 - Size = SectorLen * SectorCount + SectorBytes = 256 + MaxSectors = 4096 + Size = SectorBytes * MaxSectors CurrentVersion = 1 ) diff --git a/blob/hash.go b/blob/hash.go new file mode 100644 index 0000000..7c70759 --- /dev/null +++ b/blob/hash.go @@ -0,0 +1,90 @@ +package blob + +import ( + "io" + + "fnd/crypto" + + "golang.org/x/crypto/blake2b" +) + +var ( + ZeroHash crypto.Hash + ZeroSectorHashes SectorHashes +) + +type SectorHashes [MaxSectors]crypto.Hash + +func (s SectorHashes) Encode(w io.Writer) error { + for _, h := range s { + if _, err := w.Write(h[:]); err != nil { + return err + } + } + return nil +} + +func (s *SectorHashes) Decode(r io.Reader) error { + var res SectorHashes + var hash crypto.Hash + for i := 0; i < len(res); i++ { + if _, err := r.Read(hash[:]); err != nil { + return err + } + res[i] = hash + } + + *s = res + return nil +} + +func (s SectorHashes) DiffWith(other SectorHashes) int { + if s == other { + return -1 + } + + var i int + for i = 0; i < len(s); i++ { + if s[i] != other[i] { + break + } + } + return i +} + +func (s SectorHashes) Tip() crypto.Hash { + var i int + for i = 0; i < MaxSectors; i++ { + if s[i] == ZeroHash { + break + } + } + if i == 0 { + return crypto.ZeroHash + } + return s[i-1] +} + +func SerialHashSector(sector Sector, prevHash crypto.Hash) crypto.Hash { + var res crypto.Hash + hasher, _ := blake2b.New256(nil) + hasher.Write(prevHash[:]) + hasher.Write(sector[:]) + h := hasher.Sum(nil) + copy(res[:], h) + return res +} + +// SerialHash returns serial hash of the contents of the reader br +func SerialHash(br io.Reader, prevHash crypto.Hash, sectorSize uint16) (SectorHashes, error) { + var res SectorHashes + var sector Sector + for i := 0; i < int(sectorSize); i++ { + if _, err := br.Read(sector[:]); err != nil { + return ZeroSectorHashes, err + } + res[i] = SerialHashSector(sector, prevHash) + prevHash = res[i] + } + return res, nil +} diff --git a/blob/io.go b/blob/io.go index bdd57be..4f966d4 100644 --- a/blob/io.go +++ b/blob/io.go @@ -27,7 +27,7 @@ func ReadBlobAt(r io.ReaderAt, b []byte, off int64) (int, error) { func ReadSector(r io.ReaderAt, id uint8) (Sector, error) { var sector Sector - _, err := r.ReadAt(sector[:], int64(id)*int64(SectorLen)) + _, err := r.ReadAt(sector[:], int64(id)*int64(SectorBytes)) return sector, err } @@ -47,8 +47,8 @@ func WriteBlobAt(w io.WriterAt, b []byte, off int64) (int, error) { return w.WriteAt(b, off) } -func WriteSector(w io.WriterAt, id uint8, sector Sector) error { - _, err := w.WriteAt(sector[:], int64(id)*int64(SectorLen)) +func WriteSector(w io.WriterAt, id uint16, sector Sector) error { + _, err := w.WriteAt(sector[:], int64(id)*int64(SectorBytes)) return err } diff --git a/blob/io_test.go b/blob/io_test.go index ab2d454..58ce9f0 100644 --- a/blob/io_test.go +++ b/blob/io_test.go @@ -110,7 +110,7 @@ func TestReadSector(t *testing.T) { }, { 255, - 16711680, + 65280, }, } r := &readerWrapper{ @@ -180,7 +180,7 @@ func TestWriteBlobAt(t *testing.T) { func TestWriteSector(t *testing.T) { tests := []struct { - id uint8 + id uint16 offset int64 }{ { @@ -189,7 +189,7 @@ func TestWriteSector(t *testing.T) { }, { 255, - 16711680, + 65280, }, } w := &writerWrapper{ diff --git a/blob/merkle.go b/blob/merkle.go deleted file mode 100644 index 006bce2..0000000 --- a/blob/merkle.go +++ /dev/null @@ -1,315 +0,0 @@ -package blob - -import ( - "bytes" - "fnd/crypto" - "github.com/pkg/errors" - "io" - "math" - "math/bits" - "strings" -) - -const ( - MerkleTreeHeight = 8 - MerkleProofLen = 8 * 32 - - SubsectorSize = 4096 - SubsectorCountBlob = Size / SubsectorSize - SubsectorCountSector = SectorLen / SubsectorSize - SubsectorProofLevel = 8 -) - -var ( - precomputes = map[crypto.Hash]crypto.Hash{ - // merkle tree levels, starting from base - // assuming 4096 byte sub-sectors - {0x68, 0x6e, 0xde, 0x92, 0x88, 0xc3, 0x91, 0xe7, 0xe0, 0x50, 0x26, 0xe5, 0x6f, 0x2f, 0x91, 0xbf, 0xd8, 0x79, 0x98, 0x7a, 0x04, 0x0e, 0xa9, 0x84, 0x45, 0xda, 0xbc, 0x76, 0xf5, 0x5b, 0x8e, 0x5f}: {0x49, 0xe4, 0xb8, 0x0d, 0x5b, 0x7d, 0x8d, 0x93, 0x22, 0x48, 0x25, 0xf2, 0x6c, 0x45, 0x98, 0x7e, 0x10, 0x7b, 0xbf, 0x2f, 0x87, 0x1d, 0x4e, 0x56, 0x36, 0xac, 0x55, 0x0f, 0xf1, 0x25, 0xe0, 0x82}, - {0x49, 0xe4, 0xb8, 0x0d, 0x5b, 0x7d, 0x8d, 0x93, 0x22, 0x48, 0x25, 0xf2, 0x6c, 0x45, 0x98, 0x7e, 0x10, 0x7b, 0xbf, 0x2f, 0x87, 0x1d, 0x4e, 0x56, 0x36, 0xac, 0x55, 0x0f, 0xf1, 0x25, 0xe0, 0x82}: {0xb7, 0x95, 0x51, 0x37, 0x10, 0x7e, 0x8c, 0x87, 0x98, 0x94, 0xa9, 0x47, 0xa2, 0x35, 0xec, 0xd2, 0xa4, 0x22, 0x5a, 0x6c, 0x82, 0x12, 0x97, 0x6e, 0x97, 0x6c, 0x50, 0x31, 0x9b, 0x59, 0x31, 0xb3}, - {0xb7, 0x95, 0x51, 0x37, 0x10, 0x7e, 0x8c, 0x87, 0x98, 0x94, 0xa9, 0x47, 0xa2, 0x35, 0xec, 0xd2, 0xa4, 0x22, 0x5a, 0x6c, 0x82, 0x12, 0x97, 0x6e, 0x97, 0x6c, 0x50, 0x31, 0x9b, 0x59, 0x31, 0xb3}: {0x47, 0x19, 0x5c, 0x0c, 0xa9, 0x4e, 0x02, 0x20, 0xcd, 0x01, 0xbe, 0x88, 0x32, 0x00, 0xfd, 0xbf, 0x71, 0x48, 0x13, 0x64, 0x94, 0x2b, 0xa1, 0xe8, 0xd3, 0xef, 0x4c, 0x9a, 0x3d, 0xc4, 0xb6, 0xa5}, - {0x47, 0x19, 0x5c, 0x0c, 0xa9, 0x4e, 0x02, 0x20, 0xcd, 0x01, 0xbe, 0x88, 0x32, 0x00, 0xfd, 0xbf, 0x71, 0x48, 0x13, 0x64, 0x94, 0x2b, 0xa1, 0xe8, 0xd3, 0xef, 0x4c, 0x9a, 0x3d, 0xc4, 0xb6, 0xa5}: {0x53, 0x2a, 0x12, 0xf0, 0x9f, 0xeb, 0xf8, 0x52, 0x14, 0x19, 0x95, 0x99, 0x73, 0xad, 0x53, 0x46, 0x94, 0x4c, 0x2b, 0x22, 0xbf, 0x76, 0x4d, 0x0e, 0x1a, 0x34, 0x25, 0x5b, 0x65, 0x64, 0xfe, 0x4b}, - {0x53, 0x2a, 0x12, 0xf0, 0x9f, 0xeb, 0xf8, 0x52, 0x14, 0x19, 0x95, 0x99, 0x73, 0xad, 0x53, 0x46, 0x94, 0x4c, 0x2b, 0x22, 0xbf, 0x76, 0x4d, 0x0e, 0x1a, 0x34, 0x25, 0x5b, 0x65, 0x64, 0xfe, 0x4b}: {0xff, 0xe2, 0xcf, 0x7e, 0xcd, 0x1b, 0x99, 0x32, 0x35, 0x74, 0x6b, 0xe2, 0x1e, 0x91, 0xc8, 0xe6, 0x1a, 0x1e, 0x22, 0xda, 0xce, 0x98, 0x50, 0x91, 0x25, 0x85, 0x41, 0x65, 0x01, 0xe9, 0x84, 0x47}, - {0xff, 0xe2, 0xcf, 0x7e, 0xcd, 0x1b, 0x99, 0x32, 0x35, 0x74, 0x6b, 0xe2, 0x1e, 0x91, 0xc8, 0xe6, 0x1a, 0x1e, 0x22, 0xda, 0xce, 0x98, 0x50, 0x91, 0x25, 0x85, 0x41, 0x65, 0x01, 0xe9, 0x84, 0x47}: {0x60, 0x52, 0x2e, 0x01, 0x34, 0x1f, 0xe9, 0x62, 0x54, 0x66, 0x8b, 0xa1, 0xbc, 0x2c, 0x79, 0xdd, 0x6f, 0xc6, 0x6b, 0x37, 0x84, 0xc2, 0xeb, 0x39, 0xd4, 0xf0, 0x73, 0x19, 0xc6, 0x23, 0x26, 0x57}, - {0x60, 0x52, 0x2e, 0x01, 0x34, 0x1f, 0xe9, 0x62, 0x54, 0x66, 0x8b, 0xa1, 0xbc, 0x2c, 0x79, 0xdd, 0x6f, 0xc6, 0x6b, 0x37, 0x84, 0xc2, 0xeb, 0x39, 0xd4, 0xf0, 0x73, 0x19, 0xc6, 0x23, 0x26, 0x57}: {0xae, 0x01, 0x82, 0xab, 0x78, 0x03, 0xfc, 0x44, 0xd0, 0x85, 0xc1, 0xc7, 0x34, 0xa5, 0x52, 0xff, 0xfd, 0xb0, 0xf7, 0x44, 0x17, 0x9f, 0x0d, 0x95, 0xbd, 0x60, 0xdd, 0x6f, 0x8f, 0x18, 0x18, 0xaf}, - {0xae, 0x01, 0x82, 0xab, 0x78, 0x03, 0xfc, 0x44, 0xd0, 0x85, 0xc1, 0xc7, 0x34, 0xa5, 0x52, 0xff, 0xfd, 0xb0, 0xf7, 0x44, 0x17, 0x9f, 0x0d, 0x95, 0xbd, 0x60, 0xdd, 0x6f, 0x8f, 0x18, 0x18, 0xaf}: {0xf3, 0x4c, 0x7d, 0x70, 0xb6, 0x52, 0xeb, 0xa4, 0x8e, 0x02, 0xb4, 0x71, 0x7e, 0x1f, 0x0a, 0x2b, 0xe9, 0x33, 0x7b, 0x07, 0x51, 0xcc, 0xf7, 0xbf, 0x36, 0x44, 0x67, 0x4c, 0xbe, 0x64, 0x23, 0xd5}, - {0xf3, 0x4c, 0x7d, 0x70, 0xb6, 0x52, 0xeb, 0xa4, 0x8e, 0x02, 0xb4, 0x71, 0x7e, 0x1f, 0x0a, 0x2b, 0xe9, 0x33, 0x7b, 0x07, 0x51, 0xcc, 0xf7, 0xbf, 0x36, 0x44, 0x67, 0x4c, 0xbe, 0x64, 0x23, 0xd5}: {0x32, 0x00, 0xb9, 0x9b, 0xfc, 0xd8, 0x2c, 0x64, 0xc8, 0x18, 0xb1, 0xa2, 0xb2, 0x6e, 0x14, 0xbf, 0x78, 0x4f, 0xe9, 0x18, 0x8a, 0x55, 0x9b, 0x6b, 0x38, 0xa6, 0xdd, 0xa4, 0xfb, 0x55, 0x31, 0x47}, - {0x32, 0x00, 0xb9, 0x9b, 0xfc, 0xd8, 0x2c, 0x64, 0xc8, 0x18, 0xb1, 0xa2, 0xb2, 0x6e, 0x14, 0xbf, 0x78, 0x4f, 0xe9, 0x18, 0x8a, 0x55, 0x9b, 0x6b, 0x38, 0xa6, 0xdd, 0xa4, 0xfb, 0x55, 0x31, 0x47}: {0xb8, 0x6b, 0xe3, 0xa8, 0xb2, 0x88, 0xb0, 0xef, 0x0b, 0x7e, 0xe5, 0xa9, 0x85, 0x2d, 0x11, 0x81, 0x67, 0xa5, 0x0c, 0x84, 0x71, 0xbf, 0xb9, 0xfb, 0x8f, 0x0c, 0x79, 0x92, 0xf4, 0x52, 0xc7, 0x9c}, - {0xb8, 0x6b, 0xe3, 0xa8, 0xb2, 0x88, 0xb0, 0xef, 0x0b, 0x7e, 0xe5, 0xa9, 0x85, 0x2d, 0x11, 0x81, 0x67, 0xa5, 0x0c, 0x84, 0x71, 0xbf, 0xb9, 0xfb, 0x8f, 0x0c, 0x79, 0x92, 0xf4, 0x52, 0xc7, 0x9c}: {0xaa, 0xbd, 0xcb, 0xb2, 0x3b, 0xfc, 0xea, 0xfe, 0x71, 0xf3, 0x83, 0x4a, 0x17, 0xec, 0x1d, 0x24, 0xbd, 0x4e, 0xf2, 0xda, 0xed, 0x68, 0xd2, 0xcc, 0xc3, 0xc2, 0x98, 0x9f, 0xd0, 0x92, 0xe3, 0x53}, - {0xaa, 0xbd, 0xcb, 0xb2, 0x3b, 0xfc, 0xea, 0xfe, 0x71, 0xf3, 0x83, 0x4a, 0x17, 0xec, 0x1d, 0x24, 0xbd, 0x4e, 0xf2, 0xda, 0xed, 0x68, 0xd2, 0xcc, 0xc3, 0xc2, 0x98, 0x9f, 0xd0, 0x92, 0xe3, 0x53}: {0x7d, 0x1e, 0x84, 0xe6, 0x2d, 0x7e, 0xc9, 0xf6, 0xbc, 0x3f, 0x88, 0x66, 0x75, 0x73, 0x3d, 0xa0, 0x6d, 0xaf, 0x02, 0x77, 0xad, 0x8f, 0xfc, 0xf7, 0xc5, 0x39, 0x93, 0x8c, 0xa5, 0xee, 0x9c, 0xf2}, - } - - zero4kSector = make([]byte, 4096) - zero4kSectorHash = crypto.Hash{0x68, 0x6e, 0xde, 0x92, 0x88, 0xc3, 0x91, 0xe7, 0xe0, 0x50, 0x26, 0xe5, 0x6f, 0x2f, 0x91, 0xbf, 0xd8, 0x79, 0x98, 0x7a, 0x04, 0x0e, 0xa9, 0x84, 0x45, 0xda, 0xbc, 0x76, 0xf5, 0x5b, 0x8e, 0x5f} - - EmptyBlobMerkleRoot = crypto.Hash{0x7d, 0x1e, 0x84, 0xe6, 0x2d, 0x7e, 0xc9, 0xf6, 0xbc, 0x3f, 0x88, 0x66, 0x75, 0x73, 0x3d, 0xa0, 0x6d, 0xaf, 0x02, 0x77, 0xad, 0x8f, 0xfc, 0xf7, 0xc5, 0x39, 0x93, 0x8c, 0xa5, 0xee, 0x9c, 0xf2} - EmptyBlobBaseHash = crypto.Hash{0x53, 0x2a, 0x12, 0xf0, 0x9f, 0xeb, 0xf8, 0x52, 0x14, 0x19, 0x95, 0x99, 0x73, 0xad, 0x53, 0x46, 0x94, 0x4c, 0x2b, 0x22, 0xbf, 0x76, 0x4d, 0x0e, 0x1a, 0x34, 0x25, 0x5b, 0x65, 0x64, 0xfe, 0x4b} - ZeroMerkleBase MerkleBase -) - -type MerkleBase [256]crypto.Hash - -func (m MerkleBase) Encode(w io.Writer) error { - for _, h := range m { - if _, err := w.Write(h[:]); err != nil { - return err - } - } - return nil -} - -func (m *MerkleBase) Decode(r io.Reader) error { - var res MerkleBase - var hash crypto.Hash - for i := 0; i < len(res); i++ { - if _, err := r.Read(hash[:]); err != nil { - return err - } - res[i] = hash - } - - *m = res - return nil -} - -func (m MerkleBase) DiffWith(other MerkleBase) []uint8 { - if m == other { - return nil - } - - var out []uint8 - for i := 0; i < len(m); i++ { - if m[i] != other[i] { - out = append(out, uint8(i)) - } - } - return out -} - -type MerkleProof [MerkleProofLen]byte - -func (m MerkleProof) Encode(w io.Writer) error { - _, err := w.Write(m[:]) - return err -} - -func (m *MerkleProof) Decode(r io.Reader) error { - var buf MerkleProof - if _, err := io.ReadFull(r, buf[:]); err != nil { - return err - } - *m = buf - return nil -} - -func MakeSectorProof(tree MerkleTree, sectorID uint8) MerkleProof { - var proof MerkleProof - var buf bytes.Buffer - pos := sectorID - for i := SubsectorProofLevel; i >= 1; i-- { - level := tree[i] - if pos%2 == 0 { - buf.Write(level[pos+1].Bytes()) - } else { - buf.Write(level[pos-1].Bytes()) - } - pos = pos / 2 - } - copy(proof[:], buf.Bytes()) - return proof -} - -func VerifySectorProof(sector Sector, sectorID uint8, merkleRoot crypto.Hash, proof MerkleProof) bool { - currHash := HashSector(sector) - pos := sectorID - pRdr := bytes.NewReader(proof[:]) - var proofHash crypto.Hash - for i := 0; i < MerkleTreeHeight; i++ { - _, err := io.ReadFull(pRdr, proofHash[:]) - if err != nil { - return false - } - if pos%2 == 0 { - currHash = hashLevel(currHash, proofHash) - } else { - currHash = hashLevel(proofHash, currHash) - } - pos = pos / 2 - } - - return currHash == merkleRoot -} - -func HashSector(sector Sector) crypto.Hash { - sectorTree, err := NewMerkleTreeFromReader(bytes.NewReader(sector[:]), SubsectorCountSector, SubsectorSize) - if err != nil { - // should never happen - panic(err) - } - return sectorTree.Root() -} - -type MerkleTree [][]crypto.Hash - -func Merkleize(br io.Reader) (MerkleTree, error) { - return NewMerkleTreeFromReader(br, SubsectorCountBlob, SubsectorSize) -} - -func MakeTreeFromBase(base MerkleBase) MerkleTree { - tree, err := newMerkleTreeFromHashedLeaves(base[:]) - if err != nil { - /// should never happen - panic(err) - } - return tree -} - -func NewMerkleTreeFromReader(r io.Reader, leafCount int, leafSize int) (MerkleTree, error) { - if leafCount > math.MaxUint32 { - return nil, errors.New("leafCount must be less than math.MaxUint32") - } - if bits.OnesCount64(uint64(leafCount)) != 1 { - return nil, errors.New("leafCount must be a power of two") - } - - buf := make([]byte, leafSize) - var base []crypto.Hash - for i := 0; i < leafCount; i++ { - if _, err := io.ReadFull(r, buf); err != nil { - return nil, err - } - base = append(base, hashLeaf(buf)) - } - return newMerkleTreeFromHashedLeaves(base) -} - -func newMerkleTreeFromHashedLeaves(base []crypto.Hash) (MerkleTree, error) { - if bits.OnesCount64(uint64(len(base))) != 1 { - return nil, errors.New("base must have a power of two length") - } - - tree := [][]crypto.Hash{ - base, - } - for len(tree[0]) > 1 { - var level []crypto.Hash - for i := 0; i < len(tree[0]); i += 2 { - left := tree[0][i] - right := tree[0][i+1] - level = append(level, hashLevel(left, right)) - } - tree = append([][]crypto.Hash{level}, tree...) - } - return tree, nil -} - -func (t MerkleTree) Root() crypto.Hash { - return t[0][0] -} - -func (t MerkleTree) ProtocolBase() MerkleBase { - var out MerkleBase - data := t.Level(8) - if len(data) != len(out) { - panic("invalid tree level") - } - for i := 0; i < len(out); i++ { - out[i] = data[i] - } - return out -} - -func (t MerkleTree) Height() int { - if len(t) == 0 { - panic("trying to get height of nil merkle tree") - } - - return len(t) - 1 -} - -func (t MerkleTree) Level(i int) []crypto.Hash { - level := t[i] - out := make([]crypto.Hash, len(level)) - for i := 0; i < len(level); i++ { - out[i] = level[i] - } - return out -} - -func (t MerkleTree) String() string { - var buf strings.Builder - for i := len(t) - 1; i >= 0; i-- { - level := t[i] - for _, hash := range level { - if i < len(t)-1 { - buf.WriteString(strings.Repeat(" ", len(t)-i)) - buf.WriteString("﹂") - } - - buf.WriteString(hash.String()) - buf.WriteRune('\n') - } - } - return buf.String() -} - -func (t MerkleTree) Encode(w io.Writer) error { - if _, err := w.Write([]byte{uint8(t.Height())}); err != nil { - return err - } - for i := len(t) - 1; i >= 0; i-- { - level := t[i] - for _, node := range level { - if _, err := w.Write(node.Bytes()); err != nil { - return err - } - } - } - return nil -} - -func (t *MerkleTree) Decode(r io.Reader) error { - heightB := make([]byte, 1, 1) - if _, err := io.ReadFull(r, heightB); err != nil { - return err - } - height := int(heightB[0]) - if height > 32 { - return errors.New("refusing decode tree over 32 levels deep") - } - toRead := 1 << uint8(height) - var tree MerkleTree - for { - var level []crypto.Hash - for i := 0; i < toRead; i++ { - var h crypto.Hash - if err := h.Decode(r); err != nil { - return err - } - level = append(level, h) - } - tree = append([][]crypto.Hash{level}, tree...) - if toRead == 1 { - break - } - toRead = toRead / 2 - } - *t = tree - return nil -} - -func hashLeaf(in []byte) crypto.Hash { - if bytes.Equal(zero4kSector, in) { - return zero4kSectorHash - } - - return crypto.Blake2B256(in) -} - -func hashLevel(left crypto.Hash, right crypto.Hash) crypto.Hash { - precompRes, hasPrecomp := precomputes[left] - if hasPrecomp && left == right { - return precompRes - } - - return crypto.Blake2B256(left[:], right[:]) -} diff --git a/blob/merkle_test.go b/blob/merkle_test.go deleted file mode 100644 index f41db34..0000000 --- a/blob/merkle_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package blob - -import ( - "bytes" - "crypto/rand" - "encoding/hex" - "fnd/testutil/testfs" - "github.com/stretchr/testify/require" - "io" - "testing" -) - -const expProof = "532a12f09febf8521419959973ad5346944c2b22bf764d0e1a34255b6564fe4b" + - "ffe2cf7ecd1b993235746be21e91c8e61a1e22dace9850912585416501e98447" + - "60522e01341fe96254668ba1bc2c79dd6fc66b3784c2eb39d4f07319c6232657" + - "ae0182ab7803fc44d085c1c734a552fffdb0f744179f0d95bd60dd6f8f1818af" + - "f34c7d70b652eba48e02b4717e1f0a2be9337b0751ccf7bf3644674cbe6423d5" + - "3200b99bfcd82c64c818b1a2b26e14bf784fe9188a559b6b38a6dda4fb553147" + - "b86be3a8b288b0ef0b7ee5a9852d118167a50c8471bfb9fb8f0c7992f452c79c" + - "aabdcbb23bfceafe71f3834a17ec1d24bd4ef2daed68d2ccc3c2989fd092e353" - -func TestEncodeDecode(t *testing.T) { - f, done := testfs.NewTempFile(t) - defer done() - - _, err := io.CopyN(f, rand.Reader, Size) - require.NoError(t, err) - - blob := newFromFile("foobar", f) - var buf bytes.Buffer - merkleTree, err := Merkleize(NewReader(blob)) - require.NoError(t, err) - require.NoError(t, merkleTree.Encode(&buf)) - require.Equal(t, 262113, buf.Len()) - - otherTree := new(MerkleTree) - require.NoError(t, otherTree.Decode(bytes.NewReader(buf.Bytes()))) - require.EqualValues(t, merkleTree, *otherTree) -} - -func TestMerkleize(t *testing.T) { - f, done := testfs.NewTempFile(t) - defer done() - _, err := io.CopyN(f, new(zeroReader), Size) - require.NoError(t, err) - - blob := newFromFile("foobar", f) - mt, err := Merkleize(NewReader(blob)) - require.NoError(t, err) - require.Equal(t, "7d1e84e62d7ec9f6bc3f886675733da06daf0277ad8ffcf7c539938ca5ee9cf2", hex.EncodeToString(mt.Root().Bytes())) - - _, err = f.WriteAt([]byte{0x01, 0x99, 0x99, 0x99, 0xff, 0xad, 0xfc, 0x11, 0x99}, int64(SectorLen*2)) - require.NoError(t, err) - - mt, err = Merkleize(NewReader(blob)) - require.NoError(t, err) - require.Equal(t, "e9aa8bdf96bd79398d64a31865d2dd4d95eee2ba54b06a4e30b85e9e838497dd", hex.EncodeToString(mt.Root().Bytes())) -} - -func TestGenProof(t *testing.T) { - f, done := testfs.NewTempFile(t) - defer done() - _, err := io.CopyN(f, new(zeroReader), Size) - require.NoError(t, err) - blob := newFromFile("foobar", f) - mt, err := Merkleize(NewReader(blob)) - require.NoError(t, err) - - proof := MakeSectorProof(mt, 0) - require.NoError(t, err) - require.Equal(t, - expProof, - hex.EncodeToString(proof[:]), - ) -} - -func TestVerProof(t *testing.T) { - var proof MerkleProof - proofB, err := hex.DecodeString(expProof) - require.NoError(t, err) - copy(proof[:], proofB) - - ok := VerifySectorProof(Sector{}, 0, EmptyBlobMerkleRoot, proof) - require.True(t, ok) -} - -func TestProtocolBase(t *testing.T) { - f, done := testfs.NewTempFile(t) - defer done() - _, err := io.CopyN(f, new(zeroReader), Size) - require.NoError(t, err) - blob := newFromFile("foobar", f) - mt, err := Merkleize(NewReader(blob)) - require.NoError(t, err) - base := mt.ProtocolBase() - - for i := 0; i < len(base); i++ { - require.Equal(t, "532a12f09febf8521419959973ad5346944c2b22bf764d0e1a34255b6564fe4b", hex.EncodeToString(base[i][:])) - } -} diff --git a/blob/seal.go b/blob/seal.go index da8755b..e18110c 100644 --- a/blob/seal.go +++ b/blob/seal.go @@ -4,10 +4,9 @@ import ( "fnd/crypto" "fnd.localhost/dwire" "golang.org/x/crypto/blake2b" - "time" ) -func SealHash(name string, ts time.Time, merkleRoot crypto.Hash, reservedRoot crypto.Hash) crypto.Hash { +func SealHash(name string, epochHeight, sectorSize uint16, sectorTipHash crypto.Hash, reservedRoot crypto.Hash) crypto.Hash { h, _ := blake2b.New256(nil) if _, err := h.Write([]byte("FNBLOB")); err != nil { panic(err) @@ -15,10 +14,13 @@ func SealHash(name string, ts time.Time, merkleRoot crypto.Hash, reservedRoot cr if err := dwire.EncodeField(h, name); err != nil { panic(err) } - if err := dwire.EncodeField(h, ts); err != nil { + if err := dwire.EncodeField(h, epochHeight); err != nil { panic(err) } - if _, err := h.Write(merkleRoot[:]); err != nil { + if err := dwire.EncodeField(h, sectorSize); err != nil { + panic(err) + } + if _, err := h.Write(sectorTipHash[:]); err != nil { panic(err) } if _, err := h.Write(reservedRoot[:]); err != nil { @@ -30,7 +32,7 @@ func SealHash(name string, ts time.Time, merkleRoot crypto.Hash, reservedRoot cr return out } -func SignSeal(signer crypto.Signer, name string, ts time.Time, merkleRoot crypto.Hash, reservedRoot crypto.Hash) (crypto.Signature, error) { - h := SealHash(name, ts, merkleRoot, reservedRoot) +func SignSeal(signer crypto.Signer, name string, epochHeight, sectorSize uint16, sectorTipHash crypto.Hash, reservedRoot crypto.Hash) (crypto.Signature, error) { + h := SealHash(name, epochHeight, sectorSize, sectorTipHash, reservedRoot) return signer.Sign(h) } diff --git a/blob/seal_test.go b/blob/seal_test.go index 3978503..66fd3cf 100644 --- a/blob/seal_test.go +++ b/blob/seal_test.go @@ -7,19 +7,20 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "testing" - "time" ) func TestSealHash(t *testing.T) { - zeroTime := time.Unix(0, 0) - h := SealHash("testname", zeroTime, crypto.ZeroHash, crypto.ZeroHash) - assert.Equal(t, "694802db0f9b9b72725cf9c108b7a496bf20e1bdcc4d7feb9c42b8df1a08823b", hex.EncodeToString(h[:])) + epochZero := uint16(0) + sectorSize := uint16(0) + h := SealHash("testname", epochZero, sectorSize, crypto.ZeroHash, crypto.ZeroHash) + assert.Equal(t, "ec3f68febf79bfe9439b37c9ae707a7e16abf02e67d1ee29fd530598571e16de", hex.EncodeToString(h[:])) } func TestSignSeal(t *testing.T) { - zeroTime := time.Unix(0, 0) + epochZero := uint16(0) + sectorSize := uint16(0) priv, _ := testcrypto.FixedKey(t) - sig, err := SignSeal(crypto.NewSECP256k1Signer(priv), "testname", zeroTime, crypto.ZeroHash, crypto.ZeroHash) + sig, err := SignSeal(crypto.NewSECP256k1Signer(priv), "testname", epochZero, sectorSize, crypto.ZeroHash, crypto.ZeroHash) require.NoError(t, err) - assert.Equal(t, "1cdb0e3aa14a5489cc1bfcf25843d6747cb9412d6200c35b69dd5fb9cb133ebc7c339ea9d5bb0cce8a9b20e84642757d9a2bc82e9e0a777a9641dd05fb9e5e4836", sig.String()) + assert.Equal(t, "1c3f284e48f72666b5631c707f53c81cc6ba79f3b92d0ae7785189f1ebf43a8174572d16e6ef38de65eb918f6468d7c0bcc04a269fc7346b9da2f29c4b3a08a695", sig.String()) } diff --git a/blob/sector.go b/blob/sector.go index 34399cc..6e383d3 100644 --- a/blob/sector.go +++ b/blob/sector.go @@ -2,7 +2,7 @@ package blob import "io" -type Sector [SectorLen]byte +type Sector [SectorBytes]byte var ZeroSector Sector diff --git a/blob/store.go b/blob/store.go index 205dad4..5ac1ed7 100644 --- a/blob/store.go +++ b/blob/store.go @@ -10,6 +10,7 @@ import ( type Store interface { Open(name string) (Blob, error) Exists(name string) (bool, error) + Reset(name string) error } type storeImpl struct { @@ -42,6 +43,34 @@ func (s *storeImpl) Exists(name string) (bool, error) { return fileExists(path.Join(s.blobsPath, PathifyName(name))) } +func (s *storeImpl) Reset(name string) error { + blobSubpath := PathifyName(name) + blobFile := path.Join(s.blobsPath, blobSubpath) + exists, err := fileExists(blobFile) + var f *os.File + if err != nil { + return err + } + if !exists { + return nil + } + err = os.Remove(path.Join(s.blobsPath, PathifyName(name))) + if err != nil { + return err + } + if err := os.MkdirAll(filepath.Dir(blobFile), 0700); err != nil { + return err + } + f, err = os.OpenFile(blobFile, os.O_RDWR|os.O_CREATE, 0666) + if err != nil { + return err + } + if err := f.Truncate(Size); err != nil { + return err + } + return nil +} + func NewInStorePath(blobsPath string, name string) (Blob, error) { blobSubpath := PathifyName(name) blobFile := path.Join(blobsPath, blobSubpath) @@ -50,6 +79,7 @@ func NewInStorePath(blobsPath string, name string) (Blob, error) { if err != nil { return nil, err } + // TODO: Open in append-only mode (O_APPEND) if exists { f, err = os.OpenFile(blobFile, os.O_RDWR, 0666) } else { diff --git a/blob/transaction.go b/blob/transaction.go index 82a4e5d..73a9341 100644 --- a/blob/transaction.go +++ b/blob/transaction.go @@ -1,11 +1,12 @@ package blob import ( - "github.com/pkg/errors" "io" "io/ioutil" "os" "sync" + + "github.com/pkg/errors" ) var ( @@ -15,8 +16,9 @@ var ( type Transaction interface { Readable + io.Seeker io.WriterAt - WriteSector(id uint8, sector Sector) error + WriteSector(sector Sector) error Truncate() error Commit() error Rollback() error @@ -26,6 +28,7 @@ type Transaction interface { type txImpl struct { name string f *os.File + sectorSize uint16 mu sync.Mutex cloner func() (*os.File, error) committer func(clone *os.File) error @@ -39,6 +42,27 @@ func (t *txImpl) Name() string { return t.name } +func (t *txImpl) Seek(off int64, whence int) (int64, error) { + if off%SectorBytes != 0 { + return 0, errors.New("seek not a multiple of sector len") + } + if off < int64(t.sectorSize)*int64(SectorBytes) { + return 0, errors.New("seek before already written sector") + } + switch whence { + case io.SeekStart: + if off > Size { + return 0, errors.New("seek beyond blob bounds") + } + t.sectorSize = uint16(off / SectorBytes) + case io.SeekCurrent: + case io.SeekEnd: + default: + panic("invalid whence") + } + return off, nil +} + func (t *txImpl) ReadSector(id uint8) (Sector, error) { t.mu.Lock() defer t.mu.Unlock() @@ -69,7 +93,7 @@ func (t *txImpl) ReadAt(p []byte, off int64) (int, error) { return ReadBlobAt(t.f, p, off) } -func (t *txImpl) WriteSector(id uint8, sector Sector) error { +func (t *txImpl) WriteSector(sector Sector) error { t.mu.Lock() defer t.mu.Unlock() if t.closed { @@ -81,7 +105,11 @@ func (t *txImpl) WriteSector(id uint8, sector Sector) error { if err := t.lazyInitialize(); err != nil { return errors.Wrap(err, "error initializing transaction") } - return WriteSector(t.f, id, sector) + if err := WriteSector(t.f, t.sectorSize, sector); err != nil { + return errors.Wrap(err, "error writing sector") + } + t.sectorSize++ + return nil } func (t *txImpl) WriteAt(p []byte, off int64) (int, error) { diff --git a/blob/transaction_test.go b/blob/transaction_test.go index ffae7a6..09358a2 100644 --- a/blob/transaction_test.go +++ b/blob/transaction_test.go @@ -99,7 +99,7 @@ func TestBlob_Transaction_Remove(t *testing.T) { return err }, func() error { - return tx.WriteSector(0, ZeroSector) + return tx.WriteSector(ZeroSector) }, func() error { _, err := tx.WriteAt(make([]byte, 8, 8), 0) @@ -176,7 +176,7 @@ func TestBlob_Transaction_Race(t *testing.T) { for i := 0; i < 255; i++ { wg.Add(1) go func(id uint8) { - _ = tx.WriteSector(id, ZeroSector) + _ = tx.WriteSector(ZeroSector) wg.Done() }(uint8(i)) } @@ -210,7 +210,7 @@ func requireTxMethodsClosed(t *testing.T, tx Transaction) { { "WriteSector", func() error { - return tx.WriteSector(0, ZeroSector) + return tx.WriteSector(ZeroSector) }, }, { diff --git a/cmd/fnd-cli/cmd/blob/info.go b/cmd/fnd-cli/cmd/blob/info.go index c75ab0a..27f4f8b 100644 --- a/cmd/fnd-cli/cmd/blob/info.go +++ b/cmd/fnd-cli/cmd/blob/info.go @@ -6,13 +6,14 @@ import ( "fnd/cli" "fnd/rpc" apiv1 "fnd/rpc/v1" + "os" + "strconv" + "strings" + "fnd.localhost/handshake/primitives" "github.com/olekukonko/tablewriter" "github.com/pkg/errors" "github.com/spf13/cobra" - "os" - "strconv" - "strings" ) var infoCmd = &cobra.Command{ @@ -36,12 +37,13 @@ var infoCmd = &cobra.Command{ table.SetHeader([]string{ "Name", "Public Key", - "Timestamp", - "Merkle Root", + "Epoch Height", + "Sector Size", + "Sector Tip Hash", "Reserved Root", - "Received At", "Signature", - "Time Bank", + "Received At", + "Banned At", }) for _, name := range names { @@ -53,12 +55,13 @@ var infoCmd = &cobra.Command{ table.Append([]string{ res.Name, hex.EncodeToString(res.PublicKey.SerializeCompressed()), - res.Timestamp.String(), - res.MerkleRoot.String(), + strconv.Itoa(int(res.EpochHeight)), + strconv.Itoa(int(res.SectorSize)), + res.SectorTipHash.String(), res.ReservedRoot.String(), - res.ReceivedAt.String(), res.Signature.String(), - strconv.Itoa(res.Timebank), + res.ReceivedAt.String(), + res.BannedAt.String(), }) } diff --git a/cmd/fnd-cli/cmd/blob/write.go b/cmd/fnd-cli/cmd/blob/write.go index ea54136..2234f29 100644 --- a/cmd/fnd-cli/cmd/blob/write.go +++ b/cmd/fnd-cli/cmd/blob/write.go @@ -4,23 +4,26 @@ import ( "bufio" "bytes" "fmt" + "fnd/blob" "fnd/cli" "fnd/rpc" apiv1 "fnd/rpc/v1" - "github.com/mattn/go-isatty" - "github.com/spf13/cobra" "io" "os" + + "github.com/mattn/go-isatty" + "github.com/spf13/cobra" ) const ( - TruncateFlag = "truncate" - BroadcastFlag = "broadcast" + BroadcastFlag = "broadcast" + ResetEpochFlag = "reset-epoch" ) var ( - truncate bool - broadcast bool + fndHome string + broadcast bool + resetEpoch bool ) var writeCmd = &cobra.Command{ @@ -28,27 +31,37 @@ var writeCmd = &cobra.Command{ Short: "Write data to the specified blob.", Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + name := args[0] + conn, err := cli.DialRPC(cmd) if err != nil { return err } + homeDir := cli.GetHomeDir(cmd) signer, err := cli.GetSigner(homeDir) if err != nil { return err } - name := args[0] wr := rpc.NewBlobWriter(apiv1.NewFootnotev1Client(conn), signer, name) if err := wr.Open(); err != nil { return err } - if truncate { - if err := wr.Truncate(); err != nil { + + if resetEpoch { + if err := wr.Reset(); err != nil { + return err + } + // For technical reasons, Open _must_ be called again after Reset + // This is to reset the initial state on the client and checkout + // the name afresh. + if err := wr.Open(); err != nil { return err } } + var rd io.Reader if len(args) < 2 { if isatty.IsTerminal(os.Stdin.Fd()) { @@ -59,14 +72,22 @@ var writeCmd = &cobra.Command{ } else { rd = bufio.NewReader(bytes.NewReader([]byte(args[1]))) } - if _, err := io.Copy(wr, rd); err != nil { - return err + var sector blob.Sector + for i := 0; i < blob.MaxSectors; i++ { + if _, err := rd.Read(sector[:]); err != nil { + if err == io.EOF { + break + } + return err + } + wr.WriteSector(sector[:]) } - if err := wr.Commit(broadcast); err != nil { + sectorTipHash, err := wr.Commit(broadcast) + if err != nil { return err } - fmt.Println("Success.") + fmt.Printf("Success. Hash: %v\n", sectorTipHash) return nil }, } @@ -87,7 +108,8 @@ func readDataTTY() []byte { } func init() { - writeCmd.Flags().BoolVar(&truncate, TruncateFlag, false, "Truncate the blob before writing") writeCmd.Flags().BoolVar(&broadcast, BroadcastFlag, true, "Broadcast data to the network upon completion") + writeCmd.Flags().BoolVar(&resetEpoch, ResetEpochFlag, false, "Increment the epoch and reset the blob before write.") + writeCmd.Flags().StringVar(&fndHome, "fnd-home", "~/.fnd", "Path to FootnoteD's home directory.") cmd.AddCommand(writeCmd) } diff --git a/cmd/fnd/cmd/start.go b/cmd/fnd/cmd/start.go index e480944..5ce7f25 100644 --- a/cmd/fnd/cmd/start.go +++ b/cmd/fnd/cmd/start.go @@ -145,7 +145,6 @@ var startCmd = &cobra.Command{ updateQueue := protocol.NewUpdateQueue(mux, db) updateQueue.MaxLen = int32(cfg.Tuning.UpdateQueue.MaxLen) - updateQueue.MinUpdateInterval = config.ConvertDuration(cfg.Tuning.Timebank.MinUpdateIntervalMS, time.Millisecond) updater := protocol.NewUpdater(mux, db, updateQueue, nameLocker, bs) updater.PollInterval = config.ConvertDuration(cfg.Tuning.Updater.PollIntervalMS, time.Millisecond) diff --git a/config/config.go b/config/config.go index 1ccbdbe..277aa8e 100644 --- a/config/config.go +++ b/config/config.go @@ -45,7 +45,6 @@ type HNSResolverConfig struct { } type TuningConfig struct { - Timebank TimebankConfig `mapstructure:"timebank"` UpdateQueue UpdateQueueConfig `mapstructure:"update_queue"` Updater UpdaterConfig `mapstructure:"updater"` Syncer SyncerConfig `mapstructure:"syncer"` @@ -56,12 +55,6 @@ type TuningConfig struct { NameSyncer NameSyncerConfig `mapstructure:"name_syncer"` } -type TimebankConfig struct { - PeriodMS int `mapstructure:"period_ms"` - MinUpdateIntervalMS int `mapstructure:"min_update_interval_ms"` - FullUpdatesPerPeriod int `mapstructure:"full_updates_per_period"` -} - type UpdateQueueConfig struct { MaxLen int `mapstructure:"max_len"` ReapIntervalMS int `mapstructure:"reap_interval_ms"` diff --git a/config/default.go b/config/default.go index bf1c761..69d2445 100644 --- a/config/default.go +++ b/config/default.go @@ -40,11 +40,6 @@ var DefaultConfig = Config{ APIKey: "", }, Tuning: TuningConfig{ - Timebank: TimebankConfig{ - PeriodMS: 86400 * 2, - MinUpdateIntervalMS: 120, - FullUpdatesPerPeriod: 2, - }, UpdateQueue: UpdateQueueConfig{ MaxLen: 1000, ReapIntervalMS: 5000, @@ -230,18 +225,6 @@ log_level = "{{.LogLevel}}" # tree base data before trying another peer. tree_base_response_timeout_ms = {{.Tuning.Syncer.TreeBaseResponseTimeoutMS}} - # Configures how fnd manages each name's timebank. The timebank is used - # to throttle blob updates. Changing these values after fnd has fully - # synced is undefined behavior. - [tuning.timebank] - # Sets how many complete blob updates (i.e., updates that change all 256 - # sectors) fnd will allow per time period. - full_updates_per_period = {{.Tuning.Timebank.FullUpdatesPerPeriod}} - # Sets the minimum amount of time between updates fnd will accept. - min_update_interval_ms = {{.Tuning.Timebank.MinUpdateIntervalMS}} - # Sets the time period over which the timebank will be calculated. - period_ms = {{.Tuning.Timebank.PeriodMS}} - # Configures how fnd enqueues blob updates. [tuning.update_queue] # Sets the maximum length of the update queue. diff --git a/go.mod b/go.mod index 73b1a69..0b92a92 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,6 @@ go 1.12 replace fnd.localhost/dwire => ./vendor/fnd.localhost/dwire -replace fnd.localhost/mstream => ./vendor/fnd.localhost/mstream - replace fnd.localhost/handshake => ./vendor/fnd.localhost/handshake require ( @@ -30,7 +28,6 @@ require ( github.com/stretchr/testify v1.5.1 github.com/syndtr/goleveldb v1.0.0 golang.org/x/crypto v0.0.0-20200422194213-44a606286825 - golang.org/x/net v0.0.0-20190923162816-aa69164e4478 golang.org/x/sync v0.0.0-20190423024810-112230192c58 golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 // indirect golang.org/x/text v0.3.2 // indirect diff --git a/go.sum b/go.sum index 77e6f54..840e420 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,4 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -fnd.localhost/dwire v1.0.1 h1:OD5D5aRahOuy0QGCHxXrZf5ZJlX0IBcT/3P0k97elpQ= -fnd.localhost/dwire v1.0.1/go.mod h1:BkHitp5E9PSDVLq5nPLXWzQFEwFSR5aNhTL7M9xeC7I= -fnd.localhost/handshake v0.0.0-20200428084808-2c986090302e h1:MgJUOlZMg4aEHfxoH7DrrOZxuP7XkHQUTZ6WHfMhaXM= -fnd.localhost/handshake v0.0.0-20200428084808-2c986090302e/go.mod h1:YVUlfqY28yFbCM7y59cmL92/VfM0EDBXKrhjgmo2OAg= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= @@ -54,6 +50,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= @@ -175,18 +172,15 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610 h1:Ygq9/SRJX9+dU0WCIICM8RkWvDw03lvB77hrhJnpxfU= -google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.22.0 h1:J0UbZOIrCAl+fpTOf8YLs4dJo8L/owV4LYVtAXQoPkw= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= diff --git a/p2p/incoming_handshake_test.go b/p2p/incoming_handshake_test.go index 7031320..6b8735b 100644 --- a/p2p/incoming_handshake_test.go +++ b/p2p/incoming_handshake_test.go @@ -61,7 +61,7 @@ func TestHandleIncomingHandshake_InvalidHelloSig(t *testing.T) { Peer: setup.outPeer, Signer: testcrypto.NewRandomSigner(), }) - require.True(t, errors.Is(err, ErrPeerClosed)) + require.True(t, errors.Is(err, ErrPeerHangup)) doneCh <- struct{}{} }() <-doneCh diff --git a/p2p/seed_test.go b/p2p/seed_test.go index 945f743..069ff66 100644 --- a/p2p/seed_test.go +++ b/p2p/seed_test.go @@ -6,10 +6,9 @@ import ( ) func TestLookupDNSSeeds(t *testing.T) { - seeds, err := ResolveDNSSeeds("seeds-test.fnd.network") + seeds, err := ResolveDNSSeeds("seeds-test.merkleblock.com") require.NoError(t, err) - require.Equal(t, 2, len(seeds)) - require.Contains(t, seeds, "10.1.0.1") - require.Contains(t, seeds, "10.1.0.2") + require.Equal(t, 1, len(seeds)) + require.Contains(t, seeds, "78.46.17.17") } diff --git a/protocol/ban_list_test.go b/protocol/ban_list_test.go index fe4a865..8e4a04f 100644 --- a/protocol/ban_list_test.go +++ b/protocol/ban_list_test.go @@ -125,7 +125,7 @@ func TestReadBanList(t *testing.T) { } func TestFetchListFile(t *testing.T) { - names, err := FetchListFile("") + names, err := FetchListFile("http://sprunge.us/GgRLMw") require.NoError(t, err) require.Equal(t, 3, len(names)) require.Equal(t, names[0], "testname") diff --git a/protocol/epoch.go b/protocol/epoch.go new file mode 100644 index 0000000..bc6c1be --- /dev/null +++ b/protocol/epoch.go @@ -0,0 +1,36 @@ +package protocol + +import ( + "time" + + "fnd.localhost/handshake/primitives" +) + +// 2020 Jan 1 00:00 UTC +var epochDate = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) + +var now = time.Now + +const ( + secondsPerHour = 60 * 60 + hoursPerWeek = 7 * 24 + weekDuration = time.Duration(hoursPerWeek * time.Hour) +) + +func modBuffer(b []byte, n int) int { + p := 256 % n + acc := 0 + + for i := 0; i < len(b); i++ { + acc = (p*acc + int(b[i])) % n + } + return acc +} + +func CurrentEpoch(name string) uint16 { + hash := primitives.HashName(name) + mod := modBuffer(hash, hoursPerWeek) + offset := mod * secondsPerHour + startDate := epochDate.Add(time.Duration(offset) * time.Second) + return uint16(int(now().Sub(startDate).Seconds()) / int(weekDuration.Seconds())) +} diff --git a/protocol/epoch_test.go b/protocol/epoch_test.go new file mode 100644 index 0000000..0172d51 --- /dev/null +++ b/protocol/epoch_test.go @@ -0,0 +1,14 @@ +package protocol + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestCurrentEpoch(t *testing.T) { + now = func() time.Time { return time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC) } + epoch := CurrentEpoch("bazinga") + assert.Equal(t, uint16(52), epoch) +} diff --git a/protocol/moderation_test.go b/protocol/moderation_test.go index 21e875a..08596bf 100644 --- a/protocol/moderation_test.go +++ b/protocol/moderation_test.go @@ -22,8 +22,8 @@ func TestIngestBanLists(t *testing.T) { require.NoError(t, bl.Close()) err = IngestBanLists(db, bs, []string{ - "", - "", + "http://sprunge.us/fKKV87", + "http://sprunge.us/Ot8MYp", }) require.NoError(t, err) diff --git a/protocol/name_importer_test.go b/protocol/name_importer_test.go index 9d4b3e0..51456ad 100644 --- a/protocol/name_importer_test.go +++ b/protocol/name_importer_test.go @@ -1,6 +1,7 @@ package protocol import ( + "encoding/base64" "fmt" "fnd/testutil/testcrypto" "github.com/stretchr/testify/require" @@ -24,7 +25,7 @@ func TestParseFNRecord(t *testing.T) { } _, expPub := testcrypto.RandKey() - rec := fmt.Sprintf("f%x", expPub.SerializeCompressed()) + rec := fmt.Sprintf("f%v", base64.StdEncoding.EncodeToString(expPub.SerializeCompressed())) actPub, err := ParseFNRecord(rec) require.NoError(t, err) require.True(t, expPub.IsEqual(actPub)) diff --git a/protocol/name_syncer.go b/protocol/name_syncer.go index 70c3339..614e1bb 100644 --- a/protocol/name_syncer.go +++ b/protocol/name_syncer.go @@ -172,7 +172,7 @@ func (ns *NameSyncer) OnSyncError(cb func(name string, err error)) util.Unsubscr func (ns *NameSyncer) syncName(info *store.NameInfo) { name := info.Name - ownTS := time.Unix(0, 0) + var epochHeight, sectorSize uint16 header, err := store.GetHeader(ns.db, info.Name) if err != nil && !errors.Is(err, leveldb.ErrNotFound) { ns.lgr.Error( @@ -182,7 +182,8 @@ func (ns *NameSyncer) syncName(info *store.NameInfo) { return } if err == nil { - ownTS = header.Timestamp + epochHeight = header.EpochHeight + sectorSize = header.SectorSize } isEnvelopeCh := make(chan bool) @@ -197,7 +198,8 @@ func (ns *NameSyncer) syncName(info *store.NameInfo) { recips, _ := p2p.BroadcastRandom(ns.mux, ns.SampleSize, &wire.UpdateReq{ Name: name, - Timestamp: ownTS, + EpochHeight: epochHeight, + SectorSize: sectorSize, }) sampleSize := len(recips) diff --git a/protocol/sector_server.go b/protocol/sector_server.go index 9010a18..cd11571 100644 --- a/protocol/sector_server.go +++ b/protocol/sector_server.go @@ -1,7 +1,7 @@ package protocol import ( - "errors" + "bytes" "fmt" "fnd/blob" "fnd/config" @@ -11,8 +11,9 @@ import ( "fnd/store" "fnd/util" "fnd/wire" - "github.com/syndtr/goleveldb/leveldb" "time" + + "github.com/syndtr/goleveldb/leveldb" ) type SectorServer struct { @@ -38,8 +39,8 @@ func NewSectorServer(mux *p2p.PeerMuxer, db *leveldb.DB, bs blob.Store, nameLock } func (s *SectorServer) Start() error { - s.mux.AddMessageHandler(p2p.PeerMessageHandlerForType(wire.MessageTypeTreeBaseReq, s.onTreeBaseReq)) - s.mux.AddMessageHandler(p2p.PeerMessageHandlerForType(wire.MessageTypeSectorReq, s.onSectorReq)) + s.mux.AddMessageHandler(p2p.PeerMessageHandlerForType(wire.MessageTypeBlobReq, s.onBlobReq)) + s.mux.AddMessageHandler(p2p.PeerMessageHandlerForType(wire.MessageTypeEquivocationProof, s.onEquivocationProof)) return nil } @@ -47,62 +48,92 @@ func (s *SectorServer) Stop() error { return nil } -func (s *SectorServer) onTreeBaseReq(peerID crypto.Hash, envelope *wire.Envelope) { - reqMsg := envelope.Message.(*wire.TreeBaseReq) +// onBlobReq handles a BlobReq and responds with a BlobRes +// +// In case the BlobReq contains SectorSize == MaxSectors, it is considered a +// special case, which is a part of the equivocation proof flow. Note that in a +// normal BlobReq/BlobRes flow, SectorSize may never be MaxSectors as it is the +// start for the expected payload. +// +// In the special case where SectorSize = MaxSectors, the BlobReq is considered +// an Equivocation Request. In response, the peer is sent an Equivocation Proof. +// +// Equivocation Proof flow: +// Syncer - sees a conflicting update +// - Writes equivocation proof locally to db +// - Sends an equivocation Update (special case where Update.SectorSize = 0) +// - Expects peers to reply with Equivocation Request i.e. BlobReq where SectorSize = MaxSectors +// - Responds with Equivocation Proof +func (s *SectorServer) onBlobReq(peerID crypto.Hash, envelope *wire.Envelope) { + reqMsg := envelope.Message.(*wire.BlobReq) lgr := s.lgr.Sub( "name", reqMsg.Name, "peer_id", peerID, ) - if !s.nameLocker.TryRLock(reqMsg.Name) { - lgr.Info("dropping diff req for busy name") - return - } - merkleBase, err := store.GetMerkleBase(s.db, reqMsg.Name) + header, err := store.GetHeader(s.db, reqMsg.Name) if err != nil { - s.nameLocker.RUnlock(reqMsg.Name) - lgr.Error("error getting merkle base", "err", err) + lgr.Error( + "failed to fetch header", + "err", err) return } - s.nameLocker.RUnlock(reqMsg.Name) - resMsg := &wire.TreeBaseRes{ - Name: reqMsg.Name, - MerkleBase: merkleBase, - } - if err := s.mux.Send(peerID, resMsg); err != nil { - lgr.Error("error serving tree base response", "err", err) + if header.EpochHeight < reqMsg.EpochHeight { + lgr.Error( + "request epoch height must be <= header epoch height", + "request_epoch_height", reqMsg.EpochHeight, + "header_epoch_height", header.EpochHeight) return } - lgr.Debug("served tree base response") -} -func (s *SectorServer) onSectorReq(peerID crypto.Hash, envelope *wire.Envelope) { - reqMsg := envelope.Message.(*wire.SectorReq) - lgr := s.lgr.Sub( - "name", reqMsg.Name, - "peer_id", peerID, - ) + // Blob request with SectorSize == blob.MaxSectors is an equivocation + // request. Respond with equivocation proof. + if reqMsg.SectorSize == blob.MaxSectors { + raw, err := store.GetEquivocationProof(s.db, reqMsg.Name) + if err != nil { + lgr.Error( + "failed to fetch equivocation proof", + "err", err) + return + } + proof := &wire.EquivocationProof{} + buf := bytes.NewReader(raw) + if err := proof.Decode(buf); err != nil { + lgr.Error( + "failed to deserialize equivocation proof", + "err", err) + return + } + if err := s.mux.Send(peerID, proof); err != nil { + s.lgr.Error("error serving equivocation proof", "err", err) + return + } + return + } if !s.nameLocker.TryRLock(reqMsg.Name) { lgr.Info("dropping sector req for busy name") return } - header, err := store.GetHeader(s.db, reqMsg.Name) - if errors.Is(err, leveldb.ErrNotFound) { - s.nameLocker.RUnlock(reqMsg.Name) - return - } - if err != nil { - lgr.Error("error getting blob header", "err", err) - s.nameLocker.RUnlock(reqMsg.Name) - return + + var prevHash crypto.Hash = blob.ZeroHash + if reqMsg.SectorSize != 0 { + hash, err := store.GetSectorHash(s.db, reqMsg.Name, reqMsg.SectorSize-1) + if err != nil { + lgr.Error( + "failed to fetch sector hash", + "err", err) + return + } + prevHash = hash } - cacheKey := fmt.Sprintf("%s:%d:%d", reqMsg.Name, header.Timestamp.Unix(), reqMsg.SectorID) + + cacheKey := fmt.Sprintf("%s:%d:%d", reqMsg.Name, reqMsg.EpochHeight, reqMsg.SectorSize) cached := s.cache.Get(cacheKey) if cached != nil { s.nameLocker.RUnlock(reqMsg.Name) - s.sendResponse(peerID, reqMsg.Name, reqMsg.SectorID, cached.(blob.Sector)) + s.sendResponse(peerID, reqMsg.Name, prevHash, cached.([]blob.Sector), header.EpochHeight, reqMsg.SectorSize, header.Signature) return } @@ -120,25 +151,33 @@ func (s *SectorServer) onSectorReq(peerID crypto.Hash, envelope *wire.Envelope) s.lgr.Error("failed to close blob", "err", err) } }() - sector, err := bl.ReadSector(reqMsg.SectorID) - if err != nil { - s.nameLocker.RUnlock(reqMsg.Name) - lgr.Error( - "failed to read sector", - "err", err, - ) - return + var sectors []blob.Sector + for i := reqMsg.SectorSize; i < header.SectorSize; i++ { + sector := &blob.Sector{} + _, err = bl.ReadAt(sector[:], int64(i)*blob.SectorBytes) + if err != nil { + s.nameLocker.RUnlock(reqMsg.Name) + lgr.Error( + "failed to read sector", + "err", err, + ) + return + } + sectors = append(sectors, *sector) } - s.cache.Set(cacheKey, sector, int64(s.CacheExpiry/time.Millisecond)) + s.cache.Set(cacheKey, sectors, int64(s.CacheExpiry/time.Millisecond)) s.nameLocker.RUnlock(reqMsg.Name) - s.sendResponse(peerID, reqMsg.Name, reqMsg.SectorID, sector) + s.sendResponse(peerID, reqMsg.Name, prevHash, sectors, header.EpochHeight, reqMsg.SectorSize, header.Signature) } -func (s *SectorServer) sendResponse(peerID crypto.Hash, name string, sectorID uint8, sector blob.Sector) { - resMsg := &wire.SectorRes{ - Name: name, - SectorID: sectorID, - Sector: sector, +func (s *SectorServer) sendResponse(peerID crypto.Hash, name string, prevHash crypto.Hash, sectors []blob.Sector, epochHeight, sectorSize uint16, signature crypto.Signature) { + resMsg := &wire.BlobRes{ + Name: name, + EpochHeight: epochHeight, + PayloadPosition: sectorSize, + PrevHash: prevHash, + Payload: sectors, + Signature: signature, } if err := s.mux.Send(peerID, resMsg); err != nil { s.lgr.Error("error serving sector response", "err", err) @@ -147,6 +186,96 @@ func (s *SectorServer) sendResponse(peerID crypto.Hash, name string, sectorID ui s.lgr.Debug( "served sector response", "peer_id", peerID, - "sector_id", sectorID, + "sector_size", sectorSize, ) } + +func (s *SectorServer) onEquivocationProof(peerID crypto.Hash, envelope *wire.Envelope) { + msg := envelope.Message.(*wire.EquivocationProof) + lgr := s.lgr.Sub( + "name", msg.Name, + "peer_id", peerID, + ) + + lgr.Trace("handling equivocation response", "name", msg.Name) + if msg.LocalEpochHeight != msg.RemoteEpochHeight { + s.lgr.Warn("unexpected epoch height", "local_epoch_height", msg.LocalEpochHeight, "remote_epoch_height", msg.RemoteEpochHeight) + return + } + + header, err := store.GetHeader(s.db, msg.Name) + if err != nil { + lgr.Error( + "failed to fetch header", + "err", err) + return + } + + bannedAt, err := store.GetHeaderBan(s.db, msg.Name) + if err != nil { + lgr.Error( + "failed to fetch header ban", + "err", err) + return + } + + // Skip if name is already in equivocated state. + if !bannedAt.IsZero() && bannedAt.Add(7*24*time.Duration(time.Hour)).After(time.Now()) { + s.lgr.Warn("name banned", "name", msg.Name) + return + } + + // Skip if equivocation proof was already used to ban. + if !bannedAt.IsZero() && header.EpochHeight == msg.RemoteEpochHeight { + s.lgr.Warn("already equivocated", "name", msg.Name) + return + } + + if header.EpochHeight > msg.RemoteEpochHeight { + s.lgr.Warn("remote epoch height must be <= header epoch heigth", "header_epoch_height", header.EpochHeight, "remote_epoch_height", msg.RemoteEpochHeight) + return + } + + if msg.LocalSectorSize != msg.RemotePayloadPosition { + s.lgr.Warn("unexpected sector size", "local_sector_size", msg.LocalSectorSize, "remote_payload_position", msg.RemotePayloadPosition) + return + } + if err := validateBlobUpdate(s.db, msg.Name, msg.LocalEpochHeight, msg.LocalSectorSize, msg.LocalSectorTipHash, msg.LocalReservedRoot, msg.LocalSignature); err != nil { + lgr.Warn("local signaure validation failed", "err", err) + return + } + sectorSize := msg.RemotePayloadPosition + uint16(len(msg.RemotePayload)) + // Additional sanity check: make sure that update does not overflow max sectors. + if int(sectorSize) > blob.MaxSectors { + lgr.Warn("received unexpected sector size", "sector_size", sectorSize, "max", blob.MaxSectors) + return + } + // Generate the current tip hash from prev hash and the payload + // sectors. + var sectorTipHash crypto.Hash = msg.RemotePrevHash + for i := 0; int(i) < len(msg.RemotePayload); i++ { + sectorTipHash = blob.SerialHashSector(msg.RemotePayload[i], sectorTipHash) + } + // Verify that the update is valid by using the recomputed + // sector size, sector tip hash and other metadata. This data + // is first hashed and the signature is validated against the + // name's pubkey. See validateBlobRes. + if err := validateBlobUpdate(s.db, msg.Name, msg.RemoteEpochHeight, sectorSize, sectorTipHash, msg.RemoteReservedRoot, msg.RemoteSignature); err != nil { + lgr.Warn("remote signaure validation failed", "err", err) + return + } + lgr.Trace("equivocation proof valid ", "name", msg.Name) + if err := store.WithTx(s.db, func(tx *leveldb.Transaction) error { + return store.SetEquivocationProofTx(tx, msg.Name, msg) + }); err != nil { + lgr.Trace("error writing equivocation proof", "err", err) + } + err = store.WithTx(s.db, func(tx *leveldb.Transaction) error { + return store.SetHeaderBan(tx, msg.Name, time.Time{}) + }) + if err != nil { + lgr.Warn("error setting header banned", "err", err) + return + } + return +} diff --git a/protocol/syncer.go b/protocol/syncer.go index bf1c635..1152e22 100644 --- a/protocol/syncer.go +++ b/protocol/syncer.go @@ -5,263 +5,271 @@ import ( "fnd/crypto" "fnd/log" "fnd/p2p" + "fnd/store" "fnd/wire" - "github.com/pkg/errors" - "sync" "time" + + "fnd.localhost/handshake/primitives" + "github.com/pkg/errors" + "github.com/syndtr/goleveldb/leveldb" ) const ( - DefaultSyncerTreeBaseResTimeout = 10 * time.Second - DefaultSyncerSectorResTimeout = 15 * time.Second + DefaultSyncerBlobResTimeout = 15 * time.Second ) var ( - ErrNoTreeBaseCandidates = errors.New("no tree base candidates") - ErrSyncerNoProgress = errors.New("sync not progressing") - ErrSyncerMaxAttempts = errors.New("reached max sync attempts") + ErrInvalidPayloadSignature = errors.New("update signature is invalid") + ErrPayloadEquivocation = errors.New("update payload is equivocated") + ErrSyncerNoProgress = errors.New("sync not progressing") + ErrSyncerMaxAttempts = errors.New("reached max sync attempts") ) -type SyncTreeBasesOpts struct { - Timeout time.Duration - Mux *p2p.PeerMuxer - Peers *PeerSet - MerkleRoot crypto.Hash - Name string -} - -func SyncTreeBases(opts *SyncTreeBasesOpts) (blob.MerkleBase, error) { - lgr := log.WithModule("tree-base-syncer") - treeBaseResCh := make(chan *wire.TreeBaseRes, 1) - iter := opts.Peers.Iterator() - var newMerkleBase blob.MerkleBase - for { - peerID, ok := iter() - if !ok { - return newMerkleBase, ErrNoTreeBaseCandidates - } - - var once sync.Once - unsubTreeBaseRes := opts.Mux.AddMessageHandler(p2p.PeerMessageHandlerForType(wire.MessageTypeTreeBaseRes, func(recvPeerID crypto.Hash, res *wire.Envelope) { - msg := res.Message.(*wire.TreeBaseRes) - if msg.Name != opts.Name { - return - } - if peerID != recvPeerID { - return - } - once.Do(func() { - treeBaseResCh <- msg - }) - })) - err := opts.Mux.Send(peerID, &wire.TreeBaseReq{ - Name: opts.Name, - }) - if err != nil { - lgr.Warn("error fetching tree base from peer, trying another", "peer_id", peerID, "err", err) - unsubTreeBaseRes() - continue - } - - timeout := 10 * time.Second - if opts.Timeout != 0 { - timeout = opts.Timeout - } - timer := time.NewTimer(timeout) - - select { - case <-timer.C: - lgr.Warn("timed out fetching tree base from peer, trying another", "peer_id", peerID) - unsubTreeBaseRes() - continue - case msg := <-treeBaseResCh: - unsubTreeBaseRes() - candMerkleTree := blob.MakeTreeFromBase(msg.MerkleBase) - if candMerkleTree.Root() != opts.MerkleRoot { - lgr.Warn("received invalid merkle base from peer, trying another", "peer_id", peerID) - continue - } - newMerkleBase = candMerkleTree.ProtocolBase() - return newMerkleBase, nil - } - } +type syncUpdate struct { + sectorTipHash crypto.Hash + reservedRoot crypto.Hash + signature crypto.Signature } type SyncSectorsOpts struct { - Timeout time.Duration - Mux *p2p.PeerMuxer - Tx blob.Transaction - Peers *PeerSet - MerkleBase blob.MerkleBase - SectorsNeeded []uint8 - Name string + Timeout time.Duration + Mux *p2p.PeerMuxer + Tx blob.Transaction + Peers *PeerSet + EpochHeight uint16 + SectorSize uint16 + PrevHash crypto.Hash + Name string + DB *leveldb.DB } -type sectorRes struct { +type payloadRes struct { peerID crypto.Hash - msg *wire.SectorRes + msg *wire.BlobRes } -type reqdSectorsMap map[uint8][33]byte - -func SyncSectors(opts *SyncSectorsOpts) error { - l := log.WithModule("sector-syncer").Sub("name", opts.Name) - tx := opts.Tx - reqdSectors := make(reqdSectorsMap) - for _, id := range opts.SectorsNeeded { - hash := opts.MerkleBase[id] - if hash == blob.EmptyBlobBaseHash { - if err := tx.WriteSector(id, blob.ZeroSector); err != nil { - return errors.Wrap(err, "error writing zero sector") - } - continue - } - reqdSectors[id] = awaitingSectorHash(id, hash) +// validateBlobUpdate validates that the provided signature matches the +// expected signature for given update metadata. The metadata commits the +// update to the latest sector size and tip hash, so effectively for a blob of +// known sector size, there exists a _unique_ compact proof of the update in +// the form of the signature. +func validateBlobUpdate(db *leveldb.DB, name string, epochHeight, sectorSize uint16, sectorTipHash crypto.Hash, reservedRoot crypto.Hash, sig crypto.Signature) error { + if err := primitives.ValidateName(name); err != nil { + return errors.Wrap(err, "update name is invalid") } - - neededLen := len(reqdSectors) - var attempts int - for { - if attempts == 3 { - return ErrSyncerMaxAttempts - } - - l.Trace("performing sync attempt", "attempts", attempts+1) - reqdSectors = syncLoop(opts, reqdSectors) - remainingLen := len(reqdSectors) - l.Info( - "synced sectors", - "received", neededLen-remainingLen, - "remaining", remainingLen, - ) - if remainingLen == 0 { - return nil - } - if neededLen == remainingLen { - return ErrSyncerNoProgress - } - neededLen = remainingLen - attempts++ + banned, err := store.NameIsBanned(db, name) + if err != nil { + return errors.Wrap(err, "error reading name ban state") + } + if banned { + return errors.New("name is banned") + } + // TODO: should we check header ban state here? + info, err := store.GetNameInfo(db, name) + if err != nil { + return errors.Wrap(err, "error reading name info") + } + h := blob.SealHash(name, epochHeight, sectorSize, sectorTipHash, reservedRoot) + if !crypto.VerifySigPub(info.PublicKey, sig, h) { + return ErrInvalidPayloadSignature } + return nil } -func syncLoop(opts *SyncSectorsOpts, reqdSectors reqdSectorsMap) reqdSectorsMap { - lgr := log.WithModule("sync-loop").Sub("name", opts.Name) - - outReqdSectors := make(map[uint8][33]byte) - for k, v := range reqdSectors { - outReqdSectors[k] = v - } - sectorReqCh := make(chan uint8) - sectorResCh := make(chan *sectorRes) - sectorProcessedCh := make(chan struct{}, 1) +// SyncSectors syncs the sectors for the options provided in opts. Syncing +// happens by sending a BlobReq and expecting a BlobRes in return. Multiple +// requests may be send to multiple peers but the first valid response will be +// considered final. +// +// An invalid BlobRes which fails validation may trigger an equivocation proof, +// which is the proof that are two conflicting updates at the same epoch and +// sector size, and this proof will be stored locally and served to peers in +// the equivocation proof flow. See sector_server.go for details. +func SyncSectors(opts *SyncSectorsOpts) (*syncUpdate, error) { + lgr := log.WithModule("payload-syncer").Sub("name", opts.Name) + errs := make(chan error) + payloadResCh := make(chan *payloadRes) + payloadProcessedCh := make(chan *syncUpdate, 1) doneCh := make(chan struct{}) - unsubRes := opts.Mux.AddMessageHandler(p2p.PeerMessageHandlerForType(wire.MessageTypeSectorRes, func(peerID crypto.Hash, envelope *wire.Envelope) { - sectorResCh <- §orRes{ + unsubRes := opts.Mux.AddMessageHandler(p2p.PeerMessageHandlerForType(wire.MessageTypeBlobRes, func(peerID crypto.Hash, envelope *wire.Envelope) { + payloadResCh <- &payloadRes{ peerID: peerID, - msg: envelope.Message.(*wire.SectorRes), + msg: envelope.Message.(*wire.BlobRes), } })) go func() { - receivedSectors := make(map[uint8]bool) - awaitingSectorID := -1 + receivedPayloads := make(map[uint16]bool) for { - select { - case id := <-sectorReqCh: - awaitingSectorID = int(id) - iter := opts.Peers.Iterator() - var sendCount int - for { - peerID, ok := iter() - if !ok { - break - } - if sendCount == 7 { - break - } - err := opts.Mux.Send(peerID, &wire.SectorReq{ - Name: opts.Name, - SectorID: id, - }) - if err != nil { - lgr.Warn("error fetching sector from peer, trying another", "peer_id", peerID, "err", err) - continue - } - lgr.Debug( - "requested sector from peer", - "id", id, - "peer_id", peerID, - ) - sendCount++ + iter := opts.Peers.Iterator() + var sendCount int + for { + peerID, ok := iter() + if !ok { + break } - case res := <-sectorResCh: + if sendCount == 7 { + break + } + err := opts.Mux.Send(peerID, &wire.BlobReq{ + Name: opts.Name, + EpochHeight: opts.EpochHeight, + SectorSize: opts.SectorSize, + }) + if err != nil { + lgr.Warn("error fetching payload from peer, trying another", "peer_id", peerID, "err", err) + continue + } + lgr.Debug( + "requested payload from peer", + "peer_id", peerID, + ) + sendCount++ + } + select { + case res := <-payloadResCh: msg := res.msg peerID := res.peerID - expHash, ok := reqdSectors[msg.SectorID] if msg.Name != opts.Name { - lgr.Trace("received sector for extraneous name", "other_name", msg.Name, "sector_id", msg.SectorID) + lgr.Trace("received payload for extraneous name", "other_name", msg.Name) continue } - if !ok { - lgr.Trace("received unnecessary sector", "sector_id", msg.SectorID, "peer_id", peerID) + if receivedPayloads[msg.PayloadPosition] { + lgr.Trace("already processed this payload", "payload_position", msg.PayloadPosition, "peer_id", peerID) continue } - if receivedSectors[msg.SectorID] { - lgr.Trace("already processed this sector", "sector_id", msg.SectorID, "peer_id", peerID) + // Verify that the remote is at the same epoch height or lower + if opts.EpochHeight > msg.EpochHeight { + lgr.Trace("received unexpected epoch height", "expected_epoch_height", opts.EpochHeight, "received_epoch_height", msg.EpochHeight) continue } - if awaitingSectorID != int(msg.SectorID) { - lgr.Trace("received unsolicited sector", "sector_id", msg.SectorID, "peer_id", peerID) + // Verify that we received the payload starting from the sector + // we requested in blob request. opts.SectorSize contains our + // current known sector size, which is what we send in blob + // request. + if opts.SectorSize != msg.PayloadPosition { + lgr.Trace("received unexpected payload position", "sector_size", opts.SectorSize, "payload_position", msg.PayloadPosition) continue } - hash := awaitingSectorHash(msg.SectorID, blob.HashSector(msg.Sector)) - if expHash != hash { - lgr.Warn("invalid sector received", "sector_id", msg.SectorID, "peer_id", peerID) + sectorSize := msg.PayloadPosition + uint16(len(msg.Payload)) + // Additional sanity check: make sure that update does not overflow max sectors. + if int(sectorSize) > blob.MaxSectors { + lgr.Trace("received unexpected sector size", "sector_size", sectorSize, "max", blob.MaxSectors) continue } - if err := opts.Tx.WriteSector(msg.SectorID, msg.Sector); err != nil { - lgr.Error("failed to write sector", "sector_id", msg.SectorID, "err", err) - continue + // Generate the current tip hash from prev hash and the payload + // sectors. + var sectorTipHash crypto.Hash = msg.PrevHash + for i := 0; int(i) < len(msg.Payload); i++ { + sectorTipHash = blob.SerialHashSector(msg.Payload[i], sectorTipHash) + } + // Verify that the update is valid by using the recomputed + // sector size, sector tip hash and other metadata. This data + // is first hashed and the signature is validated against the + // name's pubkey. See validateBlobRes. + // TODO: store the latest tip hash + if err := validateBlobUpdate(opts.DB, msg.Name, msg.EpochHeight, sectorSize, sectorTipHash, msg.ReservedRoot, msg.Signature); err != nil { + lgr.Trace("blob res validation failed", "err", err) + // If prev hash matches, we have an invalid signature, + // which cannot be used as a proof of equivocation. + // TODO: ban the peer as it is clearly sending invalid data + errs <- errors.Wrap(ErrInvalidPayloadSignature, "signature validation failed") + break + } + // Verify that the prev hash from the remote matches our + // current tip hash i.e. the update starts _after_ our + // latest sector and both the sector hashes match. A + // mismatch indicates a proof of equivocation. + if opts.PrevHash != msg.PrevHash { + lgr.Trace("received unexpected prev hash", "expected_prev_hash", opts.PrevHash, "received_prev_hash", msg.PrevHash) + if opts.EpochHeight == msg.EpochHeight { + // Skip if equivocation already exists + if _, err := store.GetEquivocationProof(opts.DB, msg.Name); err == nil { + lgr.Trace("skipping update, equivocation exists") + errs <- ErrPayloadEquivocation + break + } + // TODO: record timestamp and ban this name + header, err := store.GetHeader(opts.DB, msg.Name) + if err != nil { + lgr.Trace("error getting header", "err", err) + break + } + err = store.WithTx(opts.DB, func(tx *leveldb.Transaction) error { + return store.SetHeaderBan(tx, msg.Name, time.Time{}) + }) + if err != nil { + lgr.Trace("error setting header banned", "err", err) + break + } + // TODO: rename A, B + if err := store.WithTx(opts.DB, func(tx *leveldb.Transaction) error { + proof := &wire.EquivocationProof{ + Name: msg.Name, + RemoteEpochHeight: msg.EpochHeight, + RemotePayloadPosition: msg.PayloadPosition, + RemotePrevHash: msg.PrevHash, + RemoteReservedRoot: msg.ReservedRoot, + RemotePayload: msg.Payload, + RemoteSignature: msg.Signature, + LocalEpochHeight: header.EpochHeight, + LocalSectorSize: header.SectorSize, + LocalSectorTipHash: header.SectorTipHash, + LocalReservedRoot: header.ReservedRoot, + LocalSignature: header.Signature, + } + return store.SetEquivocationProofTx(tx, msg.Name, proof) + }); err != nil { + lgr.Trace("error writing equivocation proof", "err", err) + } + update := &wire.Update{ + Name: msg.Name, + EpochHeight: msg.EpochHeight, + SectorSize: 0, + } + p2p.GossipAll(opts.Mux, update) + } + errs <- ErrPayloadEquivocation + break + } + for i := 0; int(i) < len(msg.Payload); i++ { + if err := opts.Tx.WriteSector(msg.Payload[i]); err != nil { + lgr.Error("failed to write payload", "payload_id", i, "err", err) + continue + } + } + receivedPayloads[msg.PayloadPosition] = true + payloadProcessedCh <- &syncUpdate{ + sectorTipHash: sectorTipHash, + reservedRoot: msg.ReservedRoot, + signature: msg.Signature, } - receivedSectors[msg.SectorID] = true - lgr.Debug( - "synced sector", - "name", opts.Name, - "sector_id", msg.SectorID, - "peer_id", peerID, - ) - awaitingSectorID = -1 - sectorProcessedCh <- struct{}{} case <-doneCh: return } } }() -sectorLoop: - for id := range reqdSectors { - timeout := time.NewTimer(opts.Timeout) - lgr.Debug("requesting sector", "id", id) - sectorReqCh <- id + var err error + var su *syncUpdate + timeout := time.NewTimer(opts.Timeout) +payloadLoop: + for { + lgr.Debug("requesting payload") select { - case <-sectorProcessedCh: - lgr.Debug("sector processed", "id", id) - delete(outReqdSectors, id) + case su = <-payloadProcessedCh: + lgr.Debug("payload processed") + break payloadLoop case <-timeout.C: - lgr.Warn("sector request timed out", "id", id) - break sectorLoop + lgr.Warn("payload request timed out") + break payloadLoop + case err = <-errs: + lgr.Warn("payload syncing failed") + break payloadLoop } } unsubRes() close(doneCh) - return outReqdSectors -} - -func awaitingSectorHash(id uint8, hash crypto.Hash) [33]byte { - var buf [33]byte - buf[0] = id - copy(buf[1:], hash[:]) - return buf + return su, err } diff --git a/protocol/syncer_test.go b/protocol/syncer_test.go deleted file mode 100644 index 1aa1eb2..0000000 --- a/protocol/syncer_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package protocol - -import ( - "bytes" - "crypto/rand" - "errors" - "fnd/blob" - "fnd/crypto" - "fnd/testutil/mockapp" - "fnd/util" - "github.com/stretchr/testify/require" - "testing" - "time" -) - -type syncTreeBasesSetup struct { - tp *mockapp.TestPeers - ls *mockapp.TestStorage - rs *mockapp.TestStorage -} - -func TestSyncTreeBases(t *testing.T) { - name := "foobar" - tests := []struct { - name string - run func(t *testing.T, setup *syncTreeBasesSetup) - }{ - { - "syncs tree bases with one valid peer", - func(t *testing.T, setup *syncTreeBasesSetup) { - ts := time.Now() - - var randSector blob.Sector - _, err := rand.Read(randSector[:]) - require.NoError(t, err) - sectorHash := blob.HashSector(randSector) - buf := new(bytes.Buffer) - buf.Write(blob.ZeroSector[:]) - buf.Write(randSector[:]) - update := mockapp.FillBlobReader( - t, - setup.rs.DB, - setup.rs.BlobStore, - setup.tp.RemoteSigner, - name, - ts, - ts, - bytes.NewReader(buf.Bytes()), - ) - - merkleBase, err := SyncTreeBases(&SyncTreeBasesOpts{ - Mux: setup.tp.LocalMux, - Peers: NewPeerSet([]crypto.Hash{ - crypto.HashPub(setup.tp.RemoteSigner.Pub()), - }), - MerkleRoot: update.MerkleRoot, - Name: name, - }) - - require.NoError(t, err) - for i, hash := range merkleBase { - if i == 1 { - require.Equal(t, sectorHash, hash) - continue - } - require.Equal(t, blob.EmptyBlobBaseHash, hash) - } - }, - }, - { - "aborts sync if all peers return invalid merkle bases", - func(t *testing.T, setup *syncTreeBasesSetup) { - ts := time.Now() - addlPeer, addlPeerDone := mockapp.ConnectAdditionalPeer(t, setup.tp.LocalSigner, setup.tp.LocalMux) - defer addlPeerDone() - addlStorage, addlStorageDone := mockapp.CreateStorage(t) - defer addlStorageDone() - addlSS := NewSectorServer(addlPeer.Mux, addlStorage.DB, addlStorage.BlobStore, util.NewMultiLocker()) - require.NoError(t, addlSS.Start()) - defer require.NoError(t, addlSS.Stop()) - // each blob will contain different data, thus yielding a - // different merkle root - mockapp.FillBlobRandom( - t, - setup.rs.DB, - setup.rs.BlobStore, - setup.tp.RemoteSigner, - name, - ts, - ts, - ) - mockapp.FillBlobRandom( - t, - addlStorage.DB, - addlStorage.BlobStore, - setup.tp.RemoteSigner, - name, - ts, - ts, - ) - - merkleBase, err := SyncTreeBases(&SyncTreeBasesOpts{ - Mux: setup.tp.LocalMux, - Peers: NewPeerSet([]crypto.Hash{ - crypto.HashPub(setup.tp.RemoteSigner.Pub()), - crypto.HashPub(addlPeer.Signer.Pub()), - }), - MerkleRoot: crypto.Rand32(), - Name: name, - }) - require.Equal(t, blob.ZeroMerkleBase, merkleBase) - require.Error(t, err) - require.True(t, errors.Is(err, ErrNoTreeBaseCandidates)) - }, - }, - { - "handles peers that time out", - func(t *testing.T, setup *syncTreeBasesSetup) { - ts := time.Now() - addlPeer, addlPeerDone := mockapp.ConnectAdditionalPeer(t, setup.tp.LocalSigner, setup.tp.LocalMux) - defer addlPeerDone() - - // trigger timeout by not configuring a sector server for the - // additional peer - - mockapp.FillBlobRandom( - t, - setup.rs.DB, - setup.rs.BlobStore, - setup.tp.RemoteSigner, - name, - ts, - ts, - ) - - merkleBase, err := SyncTreeBases(&SyncTreeBasesOpts{ - Timeout: 250 * time.Millisecond, - Mux: setup.tp.LocalMux, - Peers: NewPeerSet([]crypto.Hash{ - crypto.HashPub(addlPeer.Signer.Pub()), - crypto.HashPub(setup.tp.RemoteSigner.Pub()), - }), - MerkleRoot: crypto.Rand32(), - Name: name, - }) - require.Equal(t, blob.ZeroMerkleBase, merkleBase) - require.Error(t, err) - require.True(t, errors.Is(err, ErrNoTreeBaseCandidates)) - }, - }, - { - "handles peer send errors", - func(t *testing.T, setup *syncTreeBasesSetup) { - ts := time.Now() - addlPeer, addlPeerDone := mockapp.ConnectAdditionalPeer(t, setup.tp.LocalSigner, setup.tp.LocalMux) - defer addlPeerDone() - - // trigger a peer send error by closing the additional peer - // before starting tree base sync - require.NoError(t, addlPeer.RemotePeer.Close()) - - mockapp.FillBlobRandom( - t, - setup.rs.DB, - setup.rs.BlobStore, - setup.tp.RemoteSigner, - name, - ts, - ts, - ) - - merkleBase, err := SyncTreeBases(&SyncTreeBasesOpts{ - Timeout: 250 * time.Millisecond, - Mux: setup.tp.LocalMux, - Peers: NewPeerSet([]crypto.Hash{ - crypto.HashPub(addlPeer.Signer.Pub()), - crypto.HashPub(setup.tp.RemoteSigner.Pub()), - }), - MerkleRoot: crypto.Rand32(), - Name: name, - }) - require.Equal(t, blob.ZeroMerkleBase, merkleBase) - require.Error(t, err) - require.True(t, errors.Is(err, ErrNoTreeBaseCandidates)) - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - testPeers, peersDone := mockapp.ConnectTestPeers(t) - defer peersDone() - remoteStorage, remoteStorageDone := mockapp.CreateStorage(t) - defer remoteStorageDone() - localStorage, localStorageDone := mockapp.CreateStorage(t) - defer localStorageDone() - remoteSS := NewSectorServer(testPeers.RemoteMux, remoteStorage.DB, remoteStorage.BlobStore, util.NewMultiLocker()) - require.NoError(t, remoteSS.Start()) - defer require.NoError(t, remoteSS.Stop()) - - tt.run(t, &syncTreeBasesSetup{ - tp: testPeers, - ls: localStorage, - rs: remoteStorage, - }) - }) - } -} diff --git a/protocol/timebank.go b/protocol/timebank.go deleted file mode 100644 index a245273..0000000 --- a/protocol/timebank.go +++ /dev/null @@ -1,39 +0,0 @@ -package protocol - -import ( - "time" -) - -type TimebankParams struct { - TimebankDuration time.Duration - MinUpdateInterval time.Duration - FullUpdatesPerPeriod int -} - -func CheckTimebank(params *TimebankParams, prevUpdateTime time.Time, prevTimebank int, sectorsNeeded int) int { - if sectorsNeeded == 0 { - return -1 - } - if sectorsNeeded > 256 { - return -1 - } - - now := time.Now() - if prevUpdateTime.After(now.Add(-1 * params.MinUpdateInterval)) { - return -1 - } - - sectorUpdatesPerPeriod := params.FullUpdatesPerPeriod * 256 - secondsSince := int(time.Since(prevUpdateTime) / time.Second) - secondsPerSector := int(params.TimebankDuration/time.Second) / sectorUpdatesPerPeriod - sectorsAvailable := prevTimebank + (secondsSince / secondsPerSector) - if sectorsAvailable > sectorUpdatesPerPeriod { - sectorsAvailable = sectorUpdatesPerPeriod - } - - if sectorsNeeded > sectorsAvailable { - return -1 - } - - return sectorsAvailable - sectorsNeeded -} diff --git a/protocol/timebank_test.go b/protocol/timebank_test.go deleted file mode 100644 index d17c820..0000000 --- a/protocol/timebank_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package protocol - -import ( - "github.com/stretchr/testify/require" - "testing" - "time" -) - -func TestCheckTimebank(t *testing.T) { - params := &TimebankParams{ - TimebankDuration: 48 * time.Hour, - MinUpdateInterval: 2 * time.Minute, - FullUpdatesPerPeriod: 2, - } - - now := time.Now() - tests := []struct { - name string - prevUpdateTime time.Time - prevTimebank int - sectorsUpdated int - newTimebank int - }{ - { - "zero bytes updated", - now, - 0, - 0, - -1, - }, - { - "more than sector count updated", - now, - 0, - 256, - -1, - }, - { - "update is within min update frequency", - now, - 0, - 1, - -1, - }, - { - "not enough time bank - one sector interval", - now.Add(-1 * 10 * time.Minute), - 0, - 3, - -1, - }, - { - "not enough time bank - multiple sector intervals", - now.Add(-1 * 24 * time.Hour), - 0, - 257, - -1, - }, - { - "enough time bank - one sector interval", - now.Add(-1 * 10 * time.Minute), - 2, - 1, - 2, - }, - { - "enough time bank - multiple sector intervals", - now.Add(-1 * 24 * time.Hour), - 0, - 100, - 156, - }, - { - "enough time bank - multiple sector intervals with high initial bank", - now.Add(-1 * 24 * time.Hour), - 512, - 100, - 412, - }, - { - "enough time bank - multiple sector intervals zero bank", - now.Add(-1 * params.TimebankDuration), - 0, - 100, - 412, - }, - { - "enough time bank - no previous update time", - time.Time{}, - 0, - 32, - 480, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.newTimebank, CheckTimebank(params, tt.prevUpdateTime, tt.prevTimebank, tt.sectorsUpdated)) - }) - } -} diff --git a/protocol/update_queue.go b/protocol/update_queue.go index f6f029f..788cc7e 100644 --- a/protocol/update_queue.go +++ b/protocol/update_queue.go @@ -1,8 +1,6 @@ package protocol import ( - "fmt" - "github.com/btcsuite/btcd/btcec" "fnd/blob" "fnd/config" "fnd/crypto" @@ -10,46 +8,45 @@ import ( "fnd/p2p" "fnd/store" "fnd/wire" - "fnd.localhost/handshake/primitives" - "github.com/pkg/errors" - "github.com/syndtr/goleveldb/leveldb" "sync" "sync/atomic" "time" + + "fnd.localhost/handshake/primitives" + "github.com/btcsuite/btcd/btcec" + "github.com/pkg/errors" + "github.com/syndtr/goleveldb/leveldb" ) var ( - ErrUpdateQueueMaxLen = errors.New("update queue is at max length") - ErrUpdateQueueIdenticalTimestamp = errors.New("timestamp is identical to stored") - ErrUpdateQueueThrottled = errors.New("update is throttled") - ErrUpdateQueueStaleTimestamp = errors.New("update is stale") - ErrUpdateQueueSpltBrain = errors.New("split brain") - ErrInitialImportIncomplete = errors.New("initial import incomplete") + ErrUpdateQueueMaxLen = errors.New("update queue is at max length") + ErrUpdateQueueSectorUpdated = errors.New("sector already updated") + ErrUpdateQueueThrottled = errors.New("update is throttled") + ErrUpdateQueueStaleSector = errors.New("sector is stale") + ErrUpdateQueueSpltBrain = errors.New("split brain") + ErrInitialImportIncomplete = errors.New("initial import incomplete") ) type UpdateQueue struct { - MaxLen int32 - MinUpdateInterval time.Duration - mux *p2p.PeerMuxer - db *leveldb.DB - entries map[string]*UpdateQueueItem - quitCh chan struct{} - queue []string - queueLen int32 - mu sync.Mutex - lgr log.Logger + MaxLen int32 + mux *p2p.PeerMuxer + db *leveldb.DB + entries map[string]*UpdateQueueItem + quitCh chan struct{} + queue []string + queueLen int32 + mu sync.Mutex + lgr log.Logger } type UpdateQueueItem struct { - PeerIDs *PeerSet - Name string - Timestamp time.Time - MerkleRoot crypto.Hash - ReservedRoot crypto.Hash - Signature crypto.Signature - Pub *btcec.PublicKey - Height int - Disposed int32 + PeerIDs *PeerSet + Name string + EpochHeight uint16 + SectorSize uint16 + Pub *btcec.PublicKey + Height int + Disposed int32 } func (u *UpdateQueueItem) Dispose() { @@ -58,13 +55,12 @@ func (u *UpdateQueueItem) Dispose() { func NewUpdateQueue(mux *p2p.PeerMuxer, db *leveldb.DB) *UpdateQueue { return &UpdateQueue{ - MaxLen: int32(config.DefaultConfig.Tuning.UpdateQueue.MaxLen), - MinUpdateInterval: config.ConvertDuration(config.DefaultConfig.Tuning.Timebank.MinUpdateIntervalMS, time.Millisecond), - mux: mux, - db: db, - entries: make(map[string]*UpdateQueueItem), - quitCh: make(chan struct{}), - lgr: log.WithModule("update-queue"), + MaxLen: int32(config.DefaultConfig.Tuning.UpdateQueue.MaxLen), + mux: mux, + db: db, + entries: make(map[string]*UpdateQueueItem), + quitCh: make(chan struct{}), + lgr: log.WithModule("update-queue"), } } @@ -86,6 +82,7 @@ func (u *UpdateQueue) Stop() error { return nil } +// TODO: prioritize equivocations first, then higher epochs and sector sizes func (u *UpdateQueue) Enqueue(peerID crypto.Hash, update *wire.Update) error { // use atomic below to prevent having to lock mu // during expensive name validation calls when @@ -102,65 +99,65 @@ func (u *UpdateQueue) Enqueue(peerID crypto.Hash, update *wire.Update) error { return ErrInitialImportIncomplete } - nameInfo, err := u.validateUpdate(update.Name, update.Timestamp, update.MerkleRoot, update.ReservedRoot, update.Signature) + if update.SectorSize == 0 { + err := u.mux.Send(peerID, &wire.BlobReq{ + Name: update.Name, + EpochHeight: update.EpochHeight, + SectorSize: blob.MaxSectors, + }) + return err + } + + if err := u.validateUpdate(update.Name); err != nil { + return err + } + + nameInfo, err := store.GetNameInfo(u.db, update.Name) if err != nil { - return errors.Wrap(err, "name failed validation") + return errors.Wrap(err, "error getting name info") } - var storedTimestamp time.Time - var headerReceivedAt time.Time + // FIXME: epochHeight? + var storedSectorSize uint16 header, err := store.GetHeader(u.db, update.Name) if err != nil && !errors.Is(err, leveldb.ErrNotFound) { return errors.Wrap(err, "error getting name header") } else if err == nil { - storedTimestamp = header.Timestamp - headerReceivedAt = header.ReceivedAt + storedSectorSize = header.SectorSize } - fmt.Println(update) - fmt.Println(update.Timestamp) - fmt.Println(storedTimestamp) - if storedTimestamp.After(update.Timestamp) { - return ErrUpdateQueueStaleTimestamp - } - if storedTimestamp.Equal(update.Timestamp) { - return ErrUpdateQueueIdenticalTimestamp + if storedSectorSize > update.SectorSize { + return ErrUpdateQueueStaleSector } - if time.Now().Sub(headerReceivedAt) < u.MinUpdateInterval { - return ErrUpdateQueueThrottled + if storedSectorSize == update.SectorSize { + return ErrUpdateQueueSectorUpdated } - u.mu.Lock() defer u.mu.Unlock() entry := u.entries[update.Name] - if entry == nil || entry.Timestamp.Before(update.Timestamp) { + if entry == nil || entry.SectorSize < update.SectorSize { u.entries[update.Name] = &UpdateQueueItem{ - PeerIDs: NewPeerSet([]crypto.Hash{peerID}), - Name: update.Name, - Timestamp: update.Timestamp, - MerkleRoot: update.MerkleRoot, - ReservedRoot: update.ReservedRoot, - Signature: update.Signature, - Pub: nameInfo.PublicKey, - Height: nameInfo.ImportHeight, + PeerIDs: NewPeerSet([]crypto.Hash{peerID}), + Name: update.Name, + EpochHeight: update.EpochHeight, + SectorSize: update.SectorSize, + Pub: nameInfo.PublicKey, + Height: nameInfo.ImportHeight, } if entry == nil { u.queue = append(u.queue, update.Name) atomic.AddInt32(&u.queueLen, 1) } - u.lgr.Info("enqueued update", "name", update.Name, "timestamp", update.Timestamp) + u.lgr.Info("enqueued update", "name", update.Name, "epoch", update.EpochHeight, "sector", update.SectorSize) return nil } - if entry.Timestamp.After(update.Timestamp) { - return ErrUpdateQueueStaleTimestamp - } - if entry.Signature != update.Signature { - return ErrUpdateQueueSpltBrain + if entry.SectorSize > update.SectorSize { + return ErrUpdateQueueStaleSector } - u.lgr.Info("enqueued update", "name", update.Name, "timestamp", update.Timestamp) + u.lgr.Info("enqueued update", "name", update.Name, "epoch", update.EpochHeight, "sector", update.SectorSize) entry.PeerIDs.Add(peerID) return nil } @@ -176,36 +173,36 @@ func (u *UpdateQueue) Dequeue() *UpdateQueueItem { ret := u.entries[name] u.queue = u.queue[1:] atomic.AddInt32(&u.queueLen, -1) + delete(u.entries, name) return ret } -func (u *UpdateQueue) validateUpdate(name string, ts time.Time, mr crypto.Hash, rr crypto.Hash, sig crypto.Signature) (*store.NameInfo, error) { +func (u *UpdateQueue) onUpdate(peerID crypto.Hash, envelope *wire.Envelope) { + update := envelope.Message.(*wire.Update) + if err := u.Enqueue(peerID, update); err != nil { + u.lgr.Info("update rejected", "name", update.Name, "reason", err) + } +} + +func (u *UpdateQueue) validateUpdate(name string) error { if err := primitives.ValidateName(name); err != nil { - return nil, errors.Wrap(err, "update name is invalid") + return errors.Wrap(err, "update name is invalid") } - banned, err := store.NameIsBanned(u.db, name) + nameBan, err := store.NameIsBanned(u.db, name) if err != nil { - return nil, errors.Wrap(err, "error reading name ban state") + return errors.Wrap(err, "error reading name ban state") } - if banned { - return nil, errors.New("name is banned") + if nameBan { + return errors.New("name is banned") } - info, err := store.GetNameInfo(u.db, name) + headerBan, err := store.GetHeaderBan(u.db, name) if err != nil { - return nil, errors.Wrap(err, "error reading name info") + return errors.Wrap(err, "error reading header ban state") } - h := blob.SealHash(name, ts, mr, rr) - if !crypto.VerifySigPub(info.PublicKey, sig, h) { - return nil, errors.New("update signature is invalid") - } - return info, nil -} - -func (u *UpdateQueue) onUpdate(peerID crypto.Hash, envelope *wire.Envelope) { - update := envelope.Message.(*wire.Update) - if err := u.Enqueue(peerID, update); err != nil { - u.lgr.Info("update rejected", "name", update.Name, "reason", err) + if !headerBan.IsZero() { + return errors.New("header is banned") } + return nil } func (u *UpdateQueue) reapDequeuedUpdates() { diff --git a/protocol/update_queue_test.go b/protocol/update_queue_test.go index c5df409..594a539 100644 --- a/protocol/update_queue_test.go +++ b/protocol/update_queue_test.go @@ -8,10 +8,11 @@ import ( "fnd/testutil" "fnd/testutil/testcrypto" "fnd/wire" - "github.com/stretchr/testify/require" - "github.com/syndtr/goleveldb/leveldb" "testing" "time" + + "github.com/stretchr/testify/require" + "github.com/syndtr/goleveldb/leveldb" ) func TestUpdateQueue_Enqueue_InvalidBeforeEnqueue(t *testing.T) { @@ -19,19 +20,22 @@ func TestUpdateQueue_Enqueue_InvalidBeforeEnqueue(t *testing.T) { defer done() identicalHeader := signHeader(t, &store.Header{ - Name: "identical", - Timestamp: time.Unix(1, 0), - ReceivedAt: time.Unix(1, 0), + Name: "identical", + EpochHeight: uint16(0), + SectorSize: uint16(1), + EpochStartAt: time.Unix(1, 0), }) throttledHeader := signHeader(t, &store.Header{ - Name: "throttled", - Timestamp: time.Unix(1, 0), - ReceivedAt: time.Now(), + Name: "throttled", + EpochHeight: uint16(0), + SectorSize: uint16(1), + EpochStartAt: time.Now(), }) staleHeader := signHeader(t, &store.Header{ - Name: "stale", - Timestamp: time.Unix(100, 0), - ReceivedAt: time.Unix(1, 0), + Name: "stale", + EpochHeight: uint16(0), + SectorSize: uint16(100), + EpochStartAt: time.Unix(1, 0), }) headers := []*store.Header{ @@ -55,7 +59,7 @@ func TestUpdateQueue_Enqueue_InvalidBeforeEnqueue(t *testing.T) { if err := store.SetNameInfoTx(tx, header.Name, pub, 10); err != nil { return err } - if err := store.SetHeaderTx(tx, header, blob.ZeroMerkleBase); err != nil { + if err := store.SetHeaderTx(tx, header, blob.ZeroSectorHashes); err != nil { return err } } @@ -70,7 +74,9 @@ func TestUpdateQueue_Enqueue_InvalidBeforeEnqueue(t *testing.T) { { "invalid name", &wire.Update{ - Name: "--not-a-good-name--", + Name: "--not-a-good-name--", + EpochHeight: 52, + SectorSize: 10, }, func(t *testing.T, err error) { require.Contains(t, err.Error(), "name is invalid") @@ -79,60 +85,23 @@ func TestUpdateQueue_Enqueue_InvalidBeforeEnqueue(t *testing.T) { { "banned name", &wire.Update{ - Name: "banned", + Name: "banned", + EpochHeight: 52, + SectorSize: 10, }, func(t *testing.T, err error) { require.Contains(t, err.Error(), "name is banned") }, }, - { - "bad signature", - &wire.Update{ - Name: identicalHeader.Name, - Timestamp: identicalHeader.Timestamp.Add(10 * time.Second), - MerkleRoot: identicalHeader.MerkleRoot, - ReservedRoot: identicalHeader.ReservedRoot, - Signature: identicalHeader.Signature, - }, - func(t *testing.T, err error) { - require.Contains(t, err.Error(), "signature is invalid") - }, - }, { "identical", &wire.Update{ - Name: identicalHeader.Name, - Timestamp: identicalHeader.Timestamp, - MerkleRoot: identicalHeader.MerkleRoot, - ReservedRoot: identicalHeader.ReservedRoot, - Signature: identicalHeader.Signature, - }, - func(t *testing.T, err error) { - require.Equal(t, ErrUpdateQueueIdenticalTimestamp, err) + Name: identicalHeader.Name, + EpochHeight: identicalHeader.EpochHeight, + SectorSize: identicalHeader.SectorSize, }, - }, - { - "throttled", - signUpdate(t, &wire.Update{ - Name: throttledHeader.Name, - Timestamp: throttledHeader.Timestamp.Add(10 * time.Second), - MerkleRoot: throttledHeader.MerkleRoot, - ReservedRoot: identicalHeader.ReservedRoot, - }), func(t *testing.T, err error) { - require.Equal(t, ErrUpdateQueueThrottled, err) - }, - }, - { - "stale", - signUpdate(t, &wire.Update{ - Name: staleHeader.Name, - Timestamp: staleHeader.Timestamp.Add(-10 * time.Second), - MerkleRoot: throttledHeader.MerkleRoot, - ReservedRoot: identicalHeader.ReservedRoot, - }), - func(t *testing.T, err error) { - require.Equal(t, ErrUpdateQueueStaleTimestamp, err) + require.Equal(t, ErrUpdateQueueSectorUpdated, err) }, }, } @@ -149,9 +118,10 @@ func TestUpdateQueue_Enqueue_InvalidAfterEnqueue(t *testing.T) { defer done() header := signHeader(t, &store.Header{ - Name: "somename", - Timestamp: time.Unix(100, 0), - ReceivedAt: time.Unix(1, 0), + Name: "somename", + EpochHeight: uint16(0), + SectorSize: uint16(100), + EpochStartAt: time.Unix(1, 0), }) _, pub := testcrypto.FixedKey(t) @@ -162,26 +132,23 @@ func TestUpdateQueue_Enqueue_InvalidAfterEnqueue(t *testing.T) { if err := store.SetNameInfoTx(tx, header.Name, pub, 10); err != nil { return err } - if err := store.SetHeaderTx(tx, header, blob.ZeroMerkleBase); err != nil { + if err := store.SetHeaderTx(tx, header, blob.ZeroSectorHashes); err != nil { return err } return nil })) queue := NewUpdateQueue(p2p.NewPeerMuxer(testutil.TestMagic, testcrypto.FixedSigner(t)), db) - require.NoError(t, queue.Enqueue(crypto.Rand32(), signUpdate(t, &wire.Update{ - Name: header.Name, - Timestamp: header.Timestamp.Add(1 * time.Second), - }))) - require.Equal(t, ErrUpdateQueueStaleTimestamp, queue.Enqueue(crypto.Rand32(), signUpdate(t, &wire.Update{ - Name: header.Name, - Timestamp: header.Timestamp.Add(-10 * time.Second), - }))) - require.Equal(t, ErrUpdateQueueSpltBrain, queue.Enqueue(crypto.Rand32(), signUpdate(t, &wire.Update{ - Name: header.Name, - Timestamp: header.Timestamp.Add(1 * time.Second), - MerkleRoot: crypto.Rand32(), - }))) + require.NoError(t, queue.Enqueue(crypto.Rand32(), &wire.Update{ + Name: header.Name, + EpochHeight: header.EpochHeight, + SectorSize: header.SectorSize + 1, + })) + require.Equal(t, ErrUpdateQueueStaleSector, queue.Enqueue(crypto.Rand32(), &wire.Update{ + Name: header.Name, + EpochHeight: header.EpochHeight, + SectorSize: header.SectorSize - 10, + })) } func TestUpdateQueue_EnqueueDequeue(t *testing.T) { @@ -189,9 +156,10 @@ func TestUpdateQueue_EnqueueDequeue(t *testing.T) { defer done() header := signHeader(t, &store.Header{ - Name: "somename", - Timestamp: time.Unix(100, 0), - ReceivedAt: time.Unix(1, 0), + Name: "somename", + EpochHeight: uint16(0), + SectorSize: uint16(100), + EpochStartAt: time.Unix(1, 0), }) _, pub := testcrypto.FixedKey(t) @@ -202,7 +170,7 @@ func TestUpdateQueue_EnqueueDequeue(t *testing.T) { if err := store.SetNameInfoTx(tx, header.Name, pub, 10); err != nil { return err } - if err := store.SetHeaderTx(tx, header, blob.ZeroMerkleBase); err != nil { + if err := store.SetHeaderTx(tx, header, blob.ZeroSectorHashes); err != nil { return err } return nil @@ -213,10 +181,11 @@ func TestUpdateQueue_EnqueueDequeue(t *testing.T) { crypto.Rand32(), } queue := NewUpdateQueue(p2p.NewPeerMuxer(testutil.TestMagic, testcrypto.FixedSigner(t)), db) - update := signUpdate(t, &wire.Update{ - Name: header.Name, - Timestamp: header.Timestamp.Add(time.Second), - }) + update := &wire.Update{ + Name: header.Name, + EpochHeight: header.EpochHeight, + SectorSize: header.SectorSize + 1, + } for _, pid := range pids { require.NoError(t, queue.Enqueue(pid, update)) } @@ -226,24 +195,15 @@ func TestUpdateQueue_EnqueueDequeue(t *testing.T) { require.True(t, item.PeerIDs.Has(pid)) } require.Equal(t, update.Name, item.Name) - require.Equal(t, update.Timestamp, item.Timestamp) - require.Equal(t, update.MerkleRoot, item.MerkleRoot) - require.Equal(t, update.ReservedRoot, item.ReservedRoot) - require.Equal(t, update.Signature, item.Signature) + require.Equal(t, update.EpochHeight, item.EpochHeight) + require.Equal(t, update.SectorSize, item.SectorSize) require.True(t, pub.IsEqual(item.Pub)) require.Equal(t, 10, item.Height) } func signHeader(t *testing.T, header *store.Header) *store.Header { - sig, err := blob.SignSeal(testcrypto.FixedSigner(t), header.Name, header.Timestamp, header.MerkleRoot, header.ReservedRoot) + sig, err := blob.SignSeal(testcrypto.FixedSigner(t), header.Name, header.EpochHeight, header.SectorSize, header.SectorTipHash, header.ReservedRoot) require.NoError(t, err) header.Signature = sig return header } - -func signUpdate(t *testing.T, update *wire.Update) *wire.Update { - sig, err := blob.SignSeal(testcrypto.FixedSigner(t), update.Name, update.Timestamp, update.MerkleRoot, update.ReservedRoot) - require.NoError(t, err) - update.Signature = sig - return update -} diff --git a/protocol/update_server.go b/protocol/update_server.go index 9250555..9178480 100644 --- a/protocol/update_server.go +++ b/protocol/update_server.go @@ -7,6 +7,7 @@ import ( "fnd/store" "fnd/util" "fnd/wire" + "github.com/pkg/errors" "github.com/syndtr/goleveldb/leveldb" ) @@ -38,7 +39,7 @@ func (u *UpdateServer) Stop() error { func (u *UpdateServer) UpdateReqHandler(peerID crypto.Hash, envelope *wire.Envelope) { msg := envelope.Message.(*wire.UpdateReq) - u.lgr.Debug("receive update req", "name", msg.Name, "ts", msg.Timestamp) + u.lgr.Debug("receive update req", "name", msg.Name, "epoch", msg.EpochHeight, "sector", msg.SectorSize) if !u.nameLocker.TryRLock(msg.Name) { if err := u.mux.Send(peerID, wire.NewNilUpdate(msg.Name)); err != nil { @@ -69,7 +70,7 @@ func (u *UpdateServer) UpdateReqHandler(peerID crypto.Hash, envelope *wire.Envel return } - if header.Timestamp.Before(msg.Timestamp) || header.Timestamp.Equal(msg.Timestamp) { + if header.SectorSize < msg.SectorSize || header.SectorSize == msg.SectorSize { if err := u.mux.Send(peerID, wire.NewNilUpdate(msg.Name)); err != nil { u.lgr.Error("error sending response to update req", "name", msg.Name, "err", err) } else { @@ -79,10 +80,9 @@ func (u *UpdateServer) UpdateReqHandler(peerID crypto.Hash, envelope *wire.Envel } err = u.mux.Send(peerID, &wire.Update{ - Name: msg.Name, - Timestamp: header.Timestamp, - MerkleRoot: header.MerkleRoot, - Signature: header.Signature, + Name: msg.Name, + EpochHeight: header.EpochHeight, + SectorSize: header.SectorSize, }) if err != nil { u.lgr.Error("error serving update", "name", msg.Name, "err", err) diff --git a/protocol/update_server_test.go b/protocol/update_server_test.go index f6bbf46..3a00131 100644 --- a/protocol/update_server_test.go +++ b/protocol/update_server_test.go @@ -9,11 +9,11 @@ import ( "fnd/testutil/testcrypto" "fnd/util" "fnd/wire" - "github.com/stretchr/testify/require" - "github.com/syndtr/goleveldb/leveldb" "io" "testing" - "time" + + "github.com/stretchr/testify/require" + "github.com/syndtr/goleveldb/leveldb" ) func TestUpdateServer(t *testing.T) { @@ -40,8 +40,9 @@ func TestUpdateServer(t *testing.T) { { "sends a nil update for locked names", &wire.UpdateReq{ - Name: "locked", - Timestamp: time.Now(), + Name: "locked", + EpochHeight: uint16(0), + SectorSize: uint16(0), }, func(t *testing.T) { require.True(t, nameLocker.TryLock("locked")) @@ -53,8 +54,9 @@ func TestUpdateServer(t *testing.T) { { "sends a nil update for unknown names", &wire.UpdateReq{ - Name: "unknown", - Timestamp: time.Now(), + Name: "unknown", + EpochHeight: uint16(0), + SectorSize: uint16(0), }, func(t *testing.T) {}, func(t *testing.T) { @@ -64,15 +66,17 @@ func TestUpdateServer(t *testing.T) { { "sends a nil update for update requests with future timestamps", &wire.UpdateReq{ - Name: "future", - Timestamp: time.Unix(10, 0), + Name: "future", + EpochHeight: uint16(0), + SectorSize: uint16(10), }, func(t *testing.T) { require.NoError(t, store.WithTx(db, func(tx *leveldb.Transaction) error { return store.SetHeaderTx(tx, &store.Header{ - Name: "future", - Timestamp: time.Unix(5, 0), - }, blob.ZeroMerkleBase) + Name: "future", + EpochHeight: uint16(0), + SectorSize: uint16(5), + }, blob.ZeroSectorHashes) })) }, func(t *testing.T) { @@ -82,15 +86,17 @@ func TestUpdateServer(t *testing.T) { { "sends a nil update for update requests with timestamps equal to stored", &wire.UpdateReq{ - Name: "equal", - Timestamp: time.Unix(10, 0), + Name: "equal", + EpochHeight: uint16(0), + SectorSize: uint16(10), }, func(t *testing.T) { require.NoError(t, store.WithTx(db, func(tx *leveldb.Transaction) error { return store.SetHeaderTx(tx, &store.Header{ - Name: "equal", - Timestamp: time.Unix(10, 0), - }, blob.ZeroMerkleBase) + Name: "equal", + EpochHeight: uint16(0), + SectorSize: uint16(10), + }, blob.ZeroSectorHashes) })) }, func(t *testing.T) { @@ -100,21 +106,24 @@ func TestUpdateServer(t *testing.T) { { "sends an update for valid update requests with past timestamps", &wire.UpdateReq{ - Name: "valid", - Timestamp: time.Unix(5, 0), + Name: "valid", + EpochHeight: uint16(0), + SectorSize: uint16(5), }, func(t *testing.T) { - ts := time.Unix(10, 0) - tree := blob.MakeTreeFromBase(blob.ZeroMerkleBase) - sig, err := blob.SignSeal(signer, "valid", ts, tree.Root(), crypto.ZeroHash) + epochHeight := uint16(0) + sectorSize := uint16(10) + sectorHashTip := blob.ZeroHash + sig, err := blob.SignSeal(signer, "valid", epochHeight, sectorSize, sectorHashTip, crypto.ZeroHash) require.NoError(t, err) require.NoError(t, store.WithTx(db, func(tx *leveldb.Transaction) error { return store.SetHeaderTx(tx, &store.Header{ - Name: "valid", - Timestamp: ts, - MerkleRoot: tree.Root(), - Signature: sig, - }, blob.ZeroMerkleBase) + Name: "valid", + EpochHeight: epochHeight, + SectorSize: sectorSize, + SectorTipHash: sectorHashTip, + Signature: sig, + }, blob.ZeroSectorHashes) })) }, func(t *testing.T) { @@ -122,10 +131,9 @@ func TestUpdateServer(t *testing.T) { require.NoError(t, err) envelope := testutil.ReceiveEnvelope(t, clientConn) require.EqualValues(t, &wire.Update{ - Name: header.Name, - Timestamp: header.Timestamp, - MerkleRoot: header.MerkleRoot, - Signature: header.Signature, + Name: header.Name, + EpochHeight: header.EpochHeight, + SectorSize: header.SectorSize, }, envelope.Message) }, }, diff --git a/protocol/updater.go b/protocol/updater.go index ef2e7f4..0a82898 100644 --- a/protocol/updater.go +++ b/protocol/updater.go @@ -3,22 +3,29 @@ package protocol import ( "fnd/blob" "fnd/config" + "fnd/crypto" "fnd/log" "fnd/p2p" "fnd/store" "fnd/util" "fnd/wire" - "github.com/pkg/errors" - "github.com/syndtr/goleveldb/leveldb" + "io" "sync" "time" + + "github.com/pkg/errors" + "github.com/syndtr/goleveldb/leveldb" ) var ( - ErrUpdaterAlreadySynchronized = errors.New("updater already synchronized") - ErrUpdaterMerkleRootMismatch = errors.New("updater merkle root mismatch") - ErrNameLocked = errors.New("name is locked") - ErrInsufficientTimebank = errors.New("insufficient timebank") + ErrUpdaterAlreadySynchronized = errors.New("updater already synchronized") + ErrUpdaterSectorTipHashMismatch = errors.New("updater sector tip hash mismatch") + ErrNameLocked = errors.New("name is locked") + ErrNameBanned = errors.New("name is banned") + ErrInvalidEpochCurrent = errors.New("name epoch invalid current") + ErrInvalidEpochThrottled = errors.New("name epoch invalid throttled") + ErrInvalidEpochBackdated = errors.New("name epoch invalid backdated") + ErrInvalidEpochFuturedated = errors.New("name epoch invalid futuredated") updaterLogger = log.WithModule("updater") ) @@ -119,109 +126,113 @@ func UpdateBlob(cfg *UpdateConfig) error { if err != nil && !errors.Is(err, leveldb.ErrNotFound) { return errors.Wrap(err, "error getting header") } - if header != nil && header.Timestamp.Equal(item.Timestamp) { + + // If the new update and is the same size or fewer reject if it is in the + // same epoch. In the future, this may be an equivocation condition + // may not be eq + if header != nil && header.EpochHeight == item.EpochHeight && header.SectorSize >= item.SectorSize { return ErrUpdaterAlreadySynchronized } - if !cfg.NameLocker.TryLock(item.Name) { - return ErrNameLocked + var prevHash crypto.Hash = blob.ZeroHash + var epochHeight, sectorSize uint16 + var epochUpdated bool = true + if header != nil { + epochHeight = header.EpochHeight + sectorSize = header.SectorSize + prevHash = header.SectorTipHash + epochUpdated = false } - defer cfg.NameLocker.Unlock(item.Name) - newMerkleBase, err := SyncTreeBases(&SyncTreeBasesOpts{ - Timeout: DefaultSyncerTreeBaseResTimeout, - Mux: cfg.Mux, - Peers: item.PeerIDs, - MerkleRoot: item.MerkleRoot, - Name: item.Name, - }) - if err != nil { - return errors.Wrap(err, "error syncing merkle base") + // header is the existing header/data in the db + // item is the new incoming update + + // The new header should have a higher or equal epoch + if item.EpochHeight < epochHeight { + return ErrInvalidEpochBackdated } - bl, err := cfg.BlobStore.Open(item.Name) + bannedAt, err := store.GetHeaderBan(cfg.DB, item.Name) if err != nil { - return errors.Wrap(err, "error getting blob") + return err } - defer func() { - if err := bl.Close(); err != nil { - updaterLogger.Error("error closing blob", "err", err) - } - }() - var sectorsNeeded []uint8 - var prevUpdateTime time.Time - var prevTimebank int - var payableSectorCount int - if header == nil { - sectorsNeeded = blob.ZeroMerkleBase.DiffWith(newMerkleBase) - } else { - base, err := store.GetMerkleBase(cfg.DB, item.Name) - if err != nil { - return errors.Wrap(err, "error getting merkle base") - } - sectorsNeeded = base.DiffWith(newMerkleBase) - prevUpdateTime = header.ReceivedAt - prevTimebank = header.Timebank - } - for _, sectorID := range sectorsNeeded { - if newMerkleBase[sectorID] == blob.EmptyBlobBaseHash { - continue - } - payableSectorCount++ - } - if payableSectorCount == 0 { - l.Debug( - "no payable sectors, truncating", - "count", len(sectorsNeeded), - ) - tx, err := bl.Transaction() - if err != nil { - return errors.Wrap(err, "error starting transaction") + // If it is higher (skip if it's appending data to the same epoch) + if header != nil && item.EpochHeight > epochHeight { + // Recovery from banning must increment the epoch by at least 2 and one + // real week since the local node banned + if !bannedAt.IsZero() { + // Banned for at least a week + if bannedAt.Add(7 * 24 * time.Duration(time.Hour)).After(time.Now()) { + return ErrNameBanned + } + + // Publisher is banned for the equivocating epoch and the next epoch + // The faulty epoch may be old or backdated, so the penalty may not be + // as large as it seems + if item.EpochHeight <= epochHeight+1 { + return ErrInvalidEpochCurrent + } } - for _, sectorID := range sectorsNeeded { - if err := tx.WriteSector(sectorID, blob.ZeroSector); err != nil { - return errors.Wrap(err, "error truncating sector") + + // If the epoch is updated less than a week ago BUT NOT the current + // epoch or the next one. The node can bank up one extra epoch just in + // case (or periodically burst and do two epochs in a week). This + // conditions is only valid if the last local epoch increment is less + // than a week old. + if time.Now().Before(header.EpochStartAt.Add(7 * 24 * time.Duration(time.Hour))) { + if item.EpochHeight < CurrentEpoch(item.Name)+1 { + return ErrInvalidEpochThrottled } } - if err := tx.Commit(); err != nil { - return errors.Wrap(err, "error committing blob") + + // Reject any epochs more than one in the future + if item.EpochHeight > CurrentEpoch(item.Name)+1 { + return ErrInvalidEpochFuturedated } - return nil + + // Sync the entire blob on epoch rollover + epochUpdated = true + sectorSize = 0 } - l.Debug( - "calculated needed sectors", - "total", len(sectorsNeeded), - "payable", payableSectorCount, - ) - newTimebank := CheckTimebank(&TimebankParams{ - TimebankDuration: 48 * time.Hour, - MinUpdateInterval: 2 * time.Minute, - FullUpdatesPerPeriod: 2, - }, prevUpdateTime, prevTimebank, payableSectorCount) - l.Debug( - "calculated new timebank", - "prev", prevTimebank, - "new", newTimebank, - ) - if newTimebank == -1 { - return ErrInsufficientTimebank + // check blob res prev hash and equivocate + + if !cfg.NameLocker.TryLock(item.Name) { + return ErrNameLocked } + defer cfg.NameLocker.Unlock(item.Name) + + bl, err := cfg.BlobStore.Open(item.Name) + if err != nil { + return errors.Wrap(err, "error getting blob") + } + defer func() { + if err := bl.Close(); err != nil { + updaterLogger.Error("error closing blob", "err", err) + } + }() tx, err := bl.Transaction() if err != nil { return errors.Wrap(err, "error starting transaction") } - err = SyncSectors(&SyncSectorsOpts{ - Timeout: DefaultSyncerSectorResTimeout, - Mux: cfg.Mux, - Tx: tx, - Peers: item.PeerIDs, - MerkleBase: newMerkleBase, - SectorsNeeded: sectorsNeeded, - Name: item.Name, + _, err = tx.Seek(int64(sectorSize)*int64(blob.SectorBytes), io.SeekStart) + if err != nil { + return errors.Wrap(err, "error seeking transaction") + } + + sectorMeta, err := SyncSectors(&SyncSectorsOpts{ + Timeout: DefaultSyncerBlobResTimeout, + Mux: cfg.Mux, + Tx: tx, + Peers: item.PeerIDs, + EpochHeight: epochHeight, + SectorSize: sectorSize, + PrevHash: prevHash, + Name: item.Name, + DB: cfg.DB, }) if err != nil { if err := tx.Rollback(); err != nil { @@ -229,31 +240,48 @@ func UpdateBlob(cfg *UpdateConfig) error { } return errors.Wrap(err, "error during sync") } - - tree, err := blob.Merkleize(blob.NewReader(tx)) + tree, err := blob.SerialHash(blob.NewReader(tx), blob.ZeroHash, item.SectorSize) if err != nil { if err := tx.Rollback(); err != nil { updaterLogger.Error("error rolling back blob transaction", "err", err) } - return errors.Wrap(err, "error calculating new blob merkle root") + return errors.Wrap(err, "error calculating new blob sector tip hash") } - if tree.Root() != item.MerkleRoot { + + if sectorMeta.sectorTipHash != tree.Tip() { if err := tx.Rollback(); err != nil { updaterLogger.Error("error rolling back blob transaction", "err", err) } - return ErrUpdaterMerkleRootMismatch + return errors.Wrap(err, "sector tip hash mismatch") + } + + var sectorsNeeded uint16 + + if header == nil { + sectorsNeeded = item.SectorSize + } else { + sectorsNeeded = item.SectorSize - header.SectorSize + } + l.Debug( + "calculated needed sectors", + "total", sectorsNeeded, + ) + + var epochStart time.Time + if epochUpdated { + epochStart = time.Now() } err = store.WithTx(cfg.DB, func(tx *leveldb.Transaction) error { return store.SetHeaderTx(tx, &store.Header{ - Name: item.Name, - Timestamp: item.Timestamp, - MerkleRoot: item.MerkleRoot, - Signature: item.Signature, - ReservedRoot: item.ReservedRoot, - ReceivedAt: time.Now(), - Timebank: newTimebank, - }, tree.ProtocolBase()) + Name: item.Name, + EpochHeight: item.EpochHeight, + SectorSize: item.SectorSize, + SectorTipHash: tree.Tip(), + Signature: sectorMeta.signature, + ReservedRoot: sectorMeta.reservedRoot, + EpochStartAt: epochStart, + }, tree) }) if err != nil { if err := tx.Rollback(); err != nil { @@ -274,11 +302,9 @@ func UpdateBlob(cfg *UpdateConfig) error { } update := &wire.Update{ - Name: item.Name, - Timestamp: item.Timestamp, - MerkleRoot: item.MerkleRoot, - Signature: item.Signature, - ReservedRoot: item.ReservedRoot, + Name: item.Name, + EpochHeight: item.EpochHeight, + SectorSize: item.SectorSize, } p2p.GossipAll(cfg.Mux, update) return nil diff --git a/protocol/updater_test.go b/protocol/updater_test.go index ba875b1..576c0aa 100644 --- a/protocol/updater_test.go +++ b/protocol/updater_test.go @@ -1,17 +1,18 @@ package protocol import ( + "crypto/rand" "errors" + "fnd/blob" "fnd/crypto" - "fnd/p2p" "fnd/store" "fnd/testutil/mockapp" "fnd/util" - "fnd/wire" - "github.com/stretchr/testify/require" - "github.com/syndtr/goleveldb/leveldb" "testing" "time" + + "github.com/stretchr/testify/require" + "github.com/syndtr/goleveldb/leveldb" ) type updaterTestSetup struct { @@ -36,7 +37,8 @@ func TestUpdater(t *testing.T) { setup.rs.BlobStore, setup.tp.RemoteSigner, name, - ts, + CurrentEpoch(name), + blob.SectorBytes, ts, ) cfg := &UpdateConfig{ @@ -48,12 +50,10 @@ func TestUpdater(t *testing.T) { PeerIDs: NewPeerSet([]crypto.Hash{ crypto.HashPub(setup.tp.RemoteSigner.Pub()), }), - Name: name, - Timestamp: update.Timestamp, - MerkleRoot: update.MerkleRoot, - ReservedRoot: update.ReservedRoot, - Signature: update.Signature, - Pub: setup.tp.RemoteSigner.Pub(), + Name: name, + EpochHeight: update.EpochHeight, + SectorSize: update.SectorSize, + Pub: setup.tp.RemoteSigner.Pub(), }, } require.NoError(t, UpdateBlob(cfg)) @@ -64,26 +64,30 @@ func TestUpdater(t *testing.T) { "syncs sectors when the local node has an older blob", func(t *testing.T, setup *updaterTestSetup) { ts := time.Now() - // insert the blob locally, ensuring that - // there will be enough time bank - mockapp.FillBlobRandom( + epochHeight := CurrentEpoch(name) + sectorSize := uint16(10) + mockapp.FillBlobReader( t, setup.ls.DB, setup.ls.BlobStore, setup.tp.RemoteSigner, name, + epochHeight, + sectorSize, ts.Add(-48*time.Hour), - ts.Add(-48*time.Hour), + mockapp.NullReader, ) // create the new blob remotely - update := mockapp.FillBlobRandom( + update := mockapp.FillBlobReader( t, setup.rs.DB, setup.rs.BlobStore, setup.tp.RemoteSigner, name, + epochHeight, + sectorSize+10, ts, - ts, + mockapp.NullReader, ) cfg := &UpdateConfig{ Mux: setup.tp.LocalMux, @@ -94,12 +98,10 @@ func TestUpdater(t *testing.T) { PeerIDs: NewPeerSet([]crypto.Hash{ crypto.HashPub(setup.tp.RemoteSigner.Pub()), }), - Name: name, - Timestamp: update.Timestamp, - MerkleRoot: update.MerkleRoot, - ReservedRoot: update.ReservedRoot, - Signature: update.Signature, - Pub: setup.tp.RemoteSigner.Pub(), + Name: name, + EpochHeight: update.EpochHeight, + SectorSize: update.SectorSize, + Pub: setup.tp.RemoteSigner.Pub(), }, } require.NoError(t, UpdateBlob(cfg)) @@ -107,17 +109,105 @@ func TestUpdater(t *testing.T) { }, }, { - "aborts sync if the new timestamp is equal to the stored timestamp", + "aborts sync when there is an equivocation", func(t *testing.T, setup *updaterTestSetup) { + require.NoError(t, store.WithTx(setup.rs.DB, func(tx *leveldb.Transaction) error { + if err := store.SetInitialImportCompleteTx(tx); err != nil { + return err + } + if err := store.SetNameInfoTx(tx, name, setup.tp.RemoteSigner.Pub(), 10); err != nil { + return err + } + return nil + })) ts := time.Now() - update := mockapp.FillBlobRandom( + epochHeight := CurrentEpoch(name) + sectorSize := uint16(10) + mockapp.FillBlobReader( t, setup.ls.DB, setup.ls.BlobStore, setup.tp.RemoteSigner, name, + epochHeight, + sectorSize, + ts.Add(-48*time.Hour), + rand.Reader, + ) + // create the new blob remotely + // this forces an equivocation because local has 10 random sectors + // and remote as 20 _different_ random sectors. Prev Hash at 10 will + // mismatch, leading to ErrInvalidPrevHash + update := mockapp.FillBlobReader( + t, + setup.rs.DB, + setup.rs.BlobStore, + setup.tp.RemoteSigner, + name, + epochHeight, + sectorSize+10, ts, + rand.Reader, + ) + cfg := &UpdateConfig{ + Mux: setup.tp.LocalMux, + DB: setup.ls.DB, + NameLocker: util.NewMultiLocker(), + BlobStore: setup.ls.BlobStore, + Item: &UpdateQueueItem{ + PeerIDs: NewPeerSet([]crypto.Hash{ + crypto.HashPub(setup.tp.RemoteSigner.Pub()), + }), + Name: name, + EpochHeight: update.EpochHeight, + SectorSize: update.SectorSize, + Pub: setup.tp.RemoteSigner.Pub(), + }, + } + err := UpdateBlob(cfg) + require.NotNil(t, err) + time.Sleep(time.Second) + require.True(t, errors.Is(err, ErrPayloadEquivocation)) + }, + }, + { + "aborts sync when there is a invalid payload signature", + func(t *testing.T, setup *updaterTestSetup) { + ts := time.Now() + epochHeight := CurrentEpoch(name) + sectorSize := uint16(10) + mockapp.FillBlobReader( + t, + setup.ls.DB, + setup.ls.BlobStore, + setup.tp.RemoteSigner, + name, + epochHeight, + sectorSize, + ts.Add(-48*time.Hour), + mockapp.NullReader, + ) + // The sectors are generated from null reader, so there won't + // be an equivocation, however we use local signer on the + // remote (instead of remote signer like other test cases + // above), so it generates an invalid signature. + require.NoError(t, store.WithTx(setup.ls.DB, func(tx *leveldb.Transaction) error { + // TODO: ideally setup a fake signer + if err := store.SetNameInfoTx(tx, name, setup.tp.LocalSigner.Pub(), 10); err != nil { + return err + } + return nil + })) + update := mockapp.FillBlobReader( + t, + setup.rs.DB, + setup.rs.BlobStore, + setup.tp.RemoteSigner, + name, + epochHeight, + sectorSize+10, ts, + mockapp.NullReader, ) cfg := &UpdateConfig{ Mux: setup.tp.LocalMux, @@ -128,16 +218,57 @@ func TestUpdater(t *testing.T) { PeerIDs: NewPeerSet([]crypto.Hash{ crypto.HashPub(setup.tp.RemoteSigner.Pub()), }), - Name: name, - Timestamp: update.Timestamp, - MerkleRoot: update.MerkleRoot, - ReservedRoot: update.ReservedRoot, - Signature: update.Signature, - Pub: setup.tp.RemoteSigner.Pub(), + Name: name, + EpochHeight: update.EpochHeight, + SectorSize: update.SectorSize, + Pub: setup.tp.RemoteSigner.Pub(), }, } err := UpdateBlob(cfg) require.NotNil(t, err) + require.True(t, errors.Is(err, ErrInvalidPayloadSignature)) + }, + }, + { + "aborts sync if the new sector size is equal to the stored sector size", + func(t *testing.T, setup *updaterTestSetup) { + ts := time.Now() + epochHeight := CurrentEpoch(name) + sectorSize := uint16(0) + update := mockapp.FillBlobRandom( + t, + setup.ls.DB, + setup.ls.BlobStore, + setup.tp.RemoteSigner, + name, + epochHeight, + sectorSize, + ts, + ) + cfg := &UpdateConfig{ + Mux: setup.tp.LocalMux, + DB: setup.ls.DB, + NameLocker: util.NewMultiLocker(), + BlobStore: setup.ls.BlobStore, + Item: &UpdateQueueItem{ + PeerIDs: NewPeerSet([]crypto.Hash{ + crypto.HashPub(setup.tp.RemoteSigner.Pub()), + }), + Name: name, + EpochHeight: update.EpochHeight, + SectorSize: update.SectorSize, + Pub: setup.tp.RemoteSigner.Pub(), + }, + } + require.NoError(t, store.WithTx(setup.ls.DB, func(tx *leveldb.Transaction) error { + return store.SetHeaderTx(tx, &store.Header{ + Name: name, + EpochHeight: epochHeight, + SectorSize: sectorSize, + }, blob.ZeroSectorHashes) + })) + err := UpdateBlob(cfg) + require.NotNil(t, err) require.True(t, errors.Is(err, ErrUpdaterAlreadySynchronized)) }, }, @@ -155,7 +286,8 @@ func TestUpdater(t *testing.T) { PeerIDs: NewPeerSet([]crypto.Hash{ crypto.HashPub(setup.tp.RemoteSigner.Pub()), }), - Name: name, + Name: name, + EpochHeight: CurrentEpoch(name), }, } err := UpdateBlob(cfg) @@ -164,28 +296,129 @@ func TestUpdater(t *testing.T) { }, }, { - "aborts sync if there is insufficient time bank to support the update", + "aborts sync if the new sector size is equal to the stored sector size", func(t *testing.T, setup *updaterTestSetup) { ts := time.Now() - // insert the blob locally, ensuring that - // there will be enough time bank - mockapp.FillBlobRandom( + epochHeight := CurrentEpoch(name) + sectorSize := uint16(0) + update := mockapp.FillBlobRandom( t, setup.ls.DB, setup.ls.BlobStore, setup.tp.RemoteSigner, name, - ts.Add(-12*time.Hour), - ts.Add(-12*time.Hour), + epochHeight, + sectorSize, + ts, ) - // create the new blob remotely + cfg := &UpdateConfig{ + Mux: setup.tp.LocalMux, + DB: setup.ls.DB, + NameLocker: util.NewMultiLocker(), + BlobStore: setup.ls.BlobStore, + Item: &UpdateQueueItem{ + PeerIDs: NewPeerSet([]crypto.Hash{ + crypto.HashPub(setup.tp.RemoteSigner.Pub()), + }), + Name: name, + EpochHeight: update.EpochHeight, + SectorSize: update.SectorSize, + Pub: setup.tp.RemoteSigner.Pub(), + }, + } + require.NoError(t, store.WithTx(setup.ls.DB, func(tx *leveldb.Transaction) error { + return store.SetHeaderTx(tx, &store.Header{ + Name: name, + EpochHeight: epochHeight, + SectorSize: sectorSize, + }, blob.ZeroSectorHashes) + })) + err := UpdateBlob(cfg) + require.NotNil(t, err) + require.True(t, errors.Is(err, ErrUpdaterAlreadySynchronized)) + }, + }, + { + "aborts sync if the name is locked", + func(t *testing.T, setup *updaterTestSetup) { + locker := util.NewMultiLocker() + require.True(t, locker.TryLock(name)) + cfg := &UpdateConfig{ + Mux: setup.tp.LocalMux, + DB: setup.ls.DB, + NameLocker: locker, + BlobStore: setup.ls.BlobStore, + Item: &UpdateQueueItem{ + PeerIDs: NewPeerSet([]crypto.Hash{ + crypto.HashPub(setup.tp.RemoteSigner.Pub()), + }), + Name: name, + EpochHeight: CurrentEpoch(name), + }, + } + err := UpdateBlob(cfg) + require.NotNil(t, err) + require.True(t, errors.Is(err, ErrNameLocked)) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + testPeers, peersDone := mockapp.ConnectTestPeers(t) + defer peersDone() + remoteStorage, remoteStorageDone := mockapp.CreateStorage(t) + defer remoteStorageDone() + localStorage, localStorageDone := mockapp.CreateStorage(t) + defer localStorageDone() + remoteSS := NewSectorServer(testPeers.RemoteMux, remoteStorage.DB, remoteStorage.BlobStore, util.NewMultiLocker()) + require.NoError(t, remoteSS.Start()) + defer require.NoError(t, remoteSS.Stop()) + localSS := NewSectorServer(testPeers.LocalMux, localStorage.DB, localStorage.BlobStore, util.NewMultiLocker()) + require.NoError(t, localSS.Start()) + defer require.NoError(t, localSS.Stop()) + go func() { + remoteUQ := NewUpdateQueue(testPeers.RemoteMux, localStorage.DB) + require.NoError(t, remoteUQ.Start()) + defer require.NoError(t, remoteUQ.Stop()) + }() + + require.NoError(t, store.WithTx(localStorage.DB, func(tx *leveldb.Transaction) error { + if err := store.SetInitialImportCompleteTx(tx); err != nil { + return err + } + if err := store.SetNameInfoTx(tx, name, testPeers.RemoteSigner.Pub(), 10); err != nil { + return err + } + return nil + })) + + tt.run(t, &updaterTestSetup{ + tp: testPeers, + ls: localStorage, + rs: remoteStorage, + }) + }) + } +} + +func TestEpoch(t *testing.T) { + name := "foobar" + tests := []struct { + name string + run func(t *testing.T, setup *updaterTestSetup) + }{ + { + "syncs sectors when the local node has never seen the name before", + func(t *testing.T, setup *updaterTestSetup) { + ts := time.Now() update := mockapp.FillBlobRandom( t, setup.rs.DB, setup.rs.BlobStore, setup.tp.RemoteSigner, name, - ts, + 0, + blob.SectorBytes, ts, ) cfg := &UpdateConfig{ @@ -197,38 +430,59 @@ func TestUpdater(t *testing.T) { PeerIDs: NewPeerSet([]crypto.Hash{ crypto.HashPub(setup.tp.RemoteSigner.Pub()), }), - Name: name, - Timestamp: update.Timestamp, - MerkleRoot: update.MerkleRoot, - ReservedRoot: update.ReservedRoot, - Signature: update.Signature, - Pub: setup.tp.RemoteSigner.Pub(), + Name: name, + EpochHeight: update.EpochHeight, + SectorSize: update.SectorSize, + Pub: setup.tp.RemoteSigner.Pub(), }, } - err := UpdateBlob(cfg) - require.Error(t, err) - require.True(t, errors.Is(err, ErrInsufficientTimebank)) + require.NoError(t, UpdateBlob(cfg)) + mockapp.RequireBlobsEqual(t, setup.ls.BlobStore, setup.rs.BlobStore, name) }, }, { - "does not gossip if the name has fewer than 10 confirmations", + "aborts sync if the name is banned", func(t *testing.T, setup *updaterTestSetup) { + cfg := &UpdateConfig{ + Mux: setup.tp.LocalMux, + DB: setup.ls.DB, + BlobStore: setup.ls.BlobStore, + Item: &UpdateQueueItem{ + PeerIDs: NewPeerSet([]crypto.Hash{ + crypto.HashPub(setup.tp.RemoteSigner.Pub()), + }), + Name: name, + EpochHeight: CurrentEpoch(name) + 1, + }, + } require.NoError(t, store.WithTx(setup.ls.DB, func(tx *leveldb.Transaction) error { - return store.SetLastNameImportHeightTx(tx, 100) + err := store.SetHeaderBan(tx, name, time.Time{}) + if err != nil { + return err + } + return store.SetHeaderTx(tx, &store.Header{ + Name: name, + EpochHeight: 0, + SectorSize: 10, + }, blob.ZeroSectorHashes) })) + err := UpdateBlob(cfg) + require.NotNil(t, err) + require.True(t, errors.Is(err, ErrNameBanned)) + }, + }, + { + "syncs sectors when the name ban has passed", + func(t *testing.T, setup *updaterTestSetup) { ts := time.Now() - updateCh := make(chan struct{}) - unsub := setup.tp.RemoteMux.AddMessageHandler(p2p.PeerMessageHandlerForType(wire.MessageTypeUpdate, func(id crypto.Hash, envelope *wire.Envelope) { - updateCh <- struct{}{} - })) - defer unsub() update := mockapp.FillBlobRandom( t, setup.rs.DB, setup.rs.BlobStore, setup.tp.RemoteSigner, name, - ts, + CurrentEpoch(name)+1, + blob.SectorBytes, ts, ) cfg := &UpdateConfig{ @@ -240,44 +494,122 @@ func TestUpdater(t *testing.T) { PeerIDs: NewPeerSet([]crypto.Hash{ crypto.HashPub(setup.tp.RemoteSigner.Pub()), }), - Name: name, - Timestamp: update.Timestamp, - MerkleRoot: update.MerkleRoot, - ReservedRoot: update.ReservedRoot, - Signature: update.Signature, - Pub: setup.tp.RemoteSigner.Pub(), - Height: 101, + Name: name, + EpochHeight: update.EpochHeight, + SectorSize: update.SectorSize, + Pub: setup.tp.RemoteSigner.Pub(), }, } + require.NoError(t, store.WithTx(setup.ls.DB, func(tx *leveldb.Transaction) error { + err := store.SetHeaderBan(tx, name, time.Now().Add(-10*24*time.Duration(time.Hour))) + if err != nil { + return err + } + return store.SetHeaderTx(tx, &store.Header{ + Name: name, + EpochHeight: 0, + SectorSize: 0, + }, blob.ZeroSectorHashes) + })) require.NoError(t, UpdateBlob(cfg)) - - timeout := time.NewTimer(100 * time.Millisecond) - select { - case <-updateCh: - t.FailNow() - case <-timeout.C: + mockapp.RequireBlobsEqual(t, setup.ls.BlobStore, setup.rs.BlobStore, name) + }, + }, + { + "aborts sync if the epoch is throttled", + func(t *testing.T, setup *updaterTestSetup) { + cfg := &UpdateConfig{ + Mux: setup.tp.LocalMux, + DB: setup.ls.DB, + NameLocker: util.NewMultiLocker(), + BlobStore: setup.ls.BlobStore, + Item: &UpdateQueueItem{ + PeerIDs: NewPeerSet([]crypto.Hash{ + crypto.HashPub(setup.tp.RemoteSigner.Pub()), + }), + Name: name, + EpochHeight: CurrentEpoch(name) - 1, + }, } + require.NoError(t, store.WithTx(setup.ls.DB, func(tx *leveldb.Transaction) error { + return store.SetHeaderTx(tx, &store.Header{ + Name: name, + EpochHeight: 0, + SectorSize: 10, + EpochStartAt: time.Now(), + }, blob.ZeroSectorHashes) + })) + err := UpdateBlob(cfg) + require.NotNil(t, err) + require.True(t, errors.Is(err, ErrInvalidEpochThrottled)) }, }, { - "gossips if the name has more than 10 confirmations", + "aborts sync if the epoch is backdated", func(t *testing.T, setup *updaterTestSetup) { + cfg := &UpdateConfig{ + Mux: setup.tp.LocalMux, + DB: setup.ls.DB, + BlobStore: setup.ls.BlobStore, + Item: &UpdateQueueItem{ + PeerIDs: NewPeerSet([]crypto.Hash{ + crypto.HashPub(setup.tp.RemoteSigner.Pub()), + }), + Name: name, + EpochHeight: 0, + }, + } require.NoError(t, store.WithTx(setup.ls.DB, func(tx *leveldb.Transaction) error { - return store.SetLastNameImportHeightTx(tx, 100) + return store.SetHeaderTx(tx, &store.Header{ + Name: name, + EpochHeight: CurrentEpoch(name), + SectorSize: 10, + }, blob.ZeroSectorHashes) })) - ts := time.Now() - updateCh := make(chan *wire.Envelope, 1) - unsub := setup.tp.RemoteMux.AddMessageHandler(p2p.PeerMessageHandlerForType(wire.MessageTypeUpdate, func(id crypto.Hash, envelope *wire.Envelope) { - updateCh <- envelope + err := UpdateBlob(cfg) + require.NotNil(t, err) + require.True(t, errors.Is(err, ErrInvalidEpochBackdated)) + }, + }, + { + "aborts sync if the epoch is futuredated", + func(t *testing.T, setup *updaterTestSetup) { + cfg := &UpdateConfig{ + Mux: setup.tp.LocalMux, + DB: setup.ls.DB, + BlobStore: setup.ls.BlobStore, + Item: &UpdateQueueItem{ + PeerIDs: NewPeerSet([]crypto.Hash{ + crypto.HashPub(setup.tp.RemoteSigner.Pub()), + }), + Name: name, + EpochHeight: CurrentEpoch(name) + 2, + }, + } + require.NoError(t, store.WithTx(setup.ls.DB, func(tx *leveldb.Transaction) error { + return store.SetHeaderTx(tx, &store.Header{ + Name: name, + EpochHeight: CurrentEpoch(name), + SectorSize: 10, + }, blob.ZeroSectorHashes) })) - defer unsub() + err := UpdateBlob(cfg) + require.NotNil(t, err) + require.True(t, errors.Is(err, ErrInvalidEpochFuturedated)) + }, + }, + { + "rewrites partial blob with new blob on epoch rollover", + func(t *testing.T, setup *updaterTestSetup) { + ts := time.Now() update := mockapp.FillBlobRandom( t, setup.rs.DB, setup.rs.BlobStore, setup.tp.RemoteSigner, name, - ts, + CurrentEpoch(name), + blob.SectorBytes, ts, ) cfg := &UpdateConfig{ @@ -289,27 +621,21 @@ func TestUpdater(t *testing.T) { PeerIDs: NewPeerSet([]crypto.Hash{ crypto.HashPub(setup.tp.RemoteSigner.Pub()), }), - Name: name, - Timestamp: update.Timestamp, - MerkleRoot: update.MerkleRoot, - ReservedRoot: update.ReservedRoot, - Signature: update.Signature, - Pub: setup.tp.RemoteSigner.Pub(), - Height: 80, + Name: name, + EpochHeight: update.EpochHeight, + SectorSize: update.SectorSize, + Pub: setup.tp.RemoteSigner.Pub(), }, } + require.NoError(t, store.WithTx(setup.ls.DB, func(tx *leveldb.Transaction) error { + return store.SetHeaderTx(tx, &store.Header{ + Name: name, + EpochHeight: CurrentEpoch(name) - 1, + SectorSize: 10, + }, blob.ZeroSectorHashes) + })) require.NoError(t, UpdateBlob(cfg)) - - timeout := time.NewTimer(250 * time.Millisecond) - select { - case envelope := <-updateCh: - msg := envelope.Message.(*wire.Update) - require.Equal(t, name, msg.Name) - require.Equal(t, update.MerkleRoot, msg.MerkleRoot) - require.Equal(t, update.Signature, msg.Signature) - case <-timeout.C: - t.Fail() - } + mockapp.RequireBlobsEqual(t, setup.ls.BlobStore, setup.rs.BlobStore, name) }, }, } @@ -325,6 +651,16 @@ func TestUpdater(t *testing.T) { require.NoError(t, remoteSS.Start()) defer require.NoError(t, remoteSS.Stop()) + require.NoError(t, store.WithTx(localStorage.DB, func(tx *leveldb.Transaction) error { + if err := store.SetInitialImportCompleteTx(tx); err != nil { + return err + } + if err := store.SetNameInfoTx(tx, name, testPeers.RemoteSigner.Pub(), 10); err != nil { + return err + } + return nil + })) + tt.run(t, &updaterTestSetup{ tp: testPeers, ls: localStorage, diff --git a/rpc/blob.go b/rpc/blob.go index 98cb01b..4bb9091 100644 --- a/rpc/blob.go +++ b/rpc/blob.go @@ -2,13 +2,14 @@ package rpc import ( "context" - "github.com/btcsuite/btcd/btcec" "fnd/crypto" apiv1 "fnd/rpc/v1" "fnd/store" - "github.com/pkg/errors" "io" "time" + + "github.com/btcsuite/btcd/btcec" + "github.com/pkg/errors" ) func GetBlobInfo(client apiv1.Footnotev1Client, name string) (*store.BlobInfo, error) { @@ -61,9 +62,9 @@ func parseBlobInfoRes(res *apiv1.BlobInfoRes) (*store.BlobInfo, error) { if err != nil { return nil, errors.Wrap(err, "error parsing public key") } - merkleRoot, err := crypto.NewHashFromBytes(res.MerkleRoot) + sectorTipHash, err := crypto.NewHashFromBytes(res.SectorTipHash) if err != nil { - return nil, errors.Wrap(err, "error parsing merkle root") + return nil, errors.Wrap(err, "error parsing sector tip hash") } reservedRoot, err := crypto.NewHashFromBytes(res.ReservedRoot) if err != nil { @@ -75,14 +76,15 @@ func parseBlobInfoRes(res *apiv1.BlobInfoRes) (*store.BlobInfo, error) { } return &store.BlobInfo{ - Name: res.Name, - PublicKey: pub, - ImportHeight: int(res.ImportHeight), - Timestamp: time.Unix(int64(res.Timestamp), 0), - MerkleRoot: merkleRoot, - ReservedRoot: reservedRoot, - ReceivedAt: time.Unix(int64(res.ReceivedAt), 0), - Signature: sig, - Timebank: int(res.Timebank), + Name: res.Name, + PublicKey: pub, + ImportHeight: int(res.ImportHeight), + EpochHeight: uint16(res.EpochHeight), + SectorSize: uint16(res.SectorSize), + SectorTipHash: sectorTipHash, + ReservedRoot: reservedRoot, + Signature: sig, + ReceivedAt: time.Unix(int64(res.ReceivedAt), 0), + BannedAt: time.Unix(int64(res.BannedAt), 0), }, nil } diff --git a/rpc/blob_writer.go b/rpc/blob_writer.go index 2cb0ef6..bf01721 100644 --- a/rpc/blob_writer.go +++ b/rpc/blob_writer.go @@ -5,19 +5,21 @@ import ( "fnd/blob" "fnd/crypto" apiv1 "fnd/rpc/v1" - "github.com/pkg/errors" - "io" "time" + + "github.com/pkg/errors" ) type BlobWriter struct { - client apiv1.Footnotev1Client - signer crypto.Signer - name string - txID uint32 - opened bool - committed bool - offset int64 + client apiv1.Footnotev1Client + signer crypto.Signer + name string + epochHeight uint16 + sectorSize uint16 + sectorTipHash crypto.Hash + txID uint32 + opened bool + committed bool } func NewBlobWriter(client apiv1.Footnotev1Client, signer crypto.Signer, name string) *BlobWriter { @@ -42,129 +44,77 @@ func (b *BlobWriter) Open() error { return errors.Wrap(err, "failed to check out blob") } b.txID = checkoutRes.TxID - b.opened = true - return nil -} - -func (b *BlobWriter) Truncate() error { - if !b.opened { - panic("writer not open") - } - if b.committed { - panic("writer committed") - } - _, err := b.client.Truncate(context.Background(), &apiv1.TruncateReq{ - TxID: b.txID, - }) + b.epochHeight = uint16(checkoutRes.EpochHeight) + b.sectorSize = uint16(checkoutRes.SectorSize) + sectorTipHash, err := crypto.NewHashFromBytes(checkoutRes.SectorTipHash) if err != nil { - return errors.Wrap(err, "error truncating blob") + return errors.Wrap(err, "failed to check out blob") } + b.sectorTipHash = sectorTipHash + b.opened = true return nil } -func (b *BlobWriter) Seek(offset int64, whence int) (int64, error) { - if !b.opened { - panic("writer not open") - } - if b.committed { - panic("writer committed") - } - - switch whence { - case io.SeekStart: - if b.offset > blob.Size { - return b.offset, errors.New("seek beyond blob bounds") - } - b.offset = offset - case io.SeekCurrent: - next := b.offset + offset - if next > blob.Size { - return b.offset, errors.New("seek beyond blob bounds") - } - b.offset = next - case io.SeekEnd: - next := blob.Size - offset - if next < 0 { - return b.offset, errors.New("seek beyond blob bounds") - } - b.offset = next - default: - panic("invalid whence") - } - return b.offset, nil -} - -func (b *BlobWriter) WriteAt(p []byte, off int64) (int, error) { +func (b *BlobWriter) WriteSector(p []byte) (crypto.Hash, error) { if !b.opened { panic("writer not open") } if b.committed { panic("writer committed") } + var sector blob.Sector + copy(sector[:], p) - var clientErr error - n := len(p) - if off+int64(n) > blob.Size { - clientErr = errors.New("write beyond blob bounds") - n = blob.Size - int(off) - } - - res, err := b.client.WriteAt(context.Background(), &apiv1.WriteAtReq{ - TxID: b.txID, - Offset: uint32(b.offset), - Data: p[:n], + res, err := b.client.WriteSector(context.Background(), &apiv1.WriteSectorReq{ + TxID: b.txID, + Data: p, }) if err != nil { - return 0, errors.Wrap(err, "error writing blob") + return blob.ZeroHash, errors.Wrap(err, "error writing blob") } if res.WriteErr != "" { - return int(res.BytesWritten), errors.Wrap(errors.New(res.WriteErr), "error writing blob") + return blob.ZeroHash, errors.Wrap(errors.New(res.WriteErr), "error writing blob") } - return n, clientErr -} -func (b *BlobWriter) Write(p []byte) (int, error) { - if len(p) == 0 { - return 0, nil - } + b.sectorSize++ + b.sectorTipHash = blob.SerialHashSector(sector, b.sectorTipHash) - n, err := b.WriteAt(p, b.offset) - b.offset += int64(n) - if err != nil { - return n, errors.Wrap(err, "error writing blob") - } - return n, nil + return b.sectorTipHash, nil +} + +func (b *BlobWriter) Reset() error { + _, err := b.client.ResetEpoch(context.Background(), &apiv1.ResetEpochReq{ + TxID: b.txID, + }) + b.opened = false + return err } -func (b *BlobWriter) Commit(broadcast bool) error { +func (b *BlobWriter) Commit(broadcast bool) (crypto.Hash, error) { if !b.opened { panic("writer not open") } if b.committed { panic("writer committed") } - precommitRes, err := b.client.PreCommit(context.Background(), &apiv1.PreCommitReq{ - TxID: b.txID, - }) - if err != nil { - return errors.Wrap(err, "error retrieving precommit") - } ts := time.Now() - var mr crypto.Hash - copy(mr[:], precommitRes.MerkleRoot) - sig, err := blob.SignSeal(b.signer, b.name, ts, mr, crypto.ZeroHash) + + sig, err := blob.SignSeal(b.signer, b.name, b.epochHeight, b.sectorSize, b.sectorTipHash, crypto.ZeroHash) if err != nil { - return errors.Wrap(err, "error sealing blob") + return blob.ZeroHash, errors.Wrap(err, "error sealing blob") } _, err = b.client.Commit(context.Background(), &apiv1.CommitReq{ - TxID: b.txID, - Timestamp: uint64(ts.Unix()), - Signature: sig[:], - Broadcast: broadcast, + TxID: b.txID, + Timestamp: uint64(ts.Unix()), + Signature: sig[:], + Broadcast: broadcast, + EpochHeight: uint32(b.epochHeight), + SectorSize: uint32(b.sectorSize), + SectorTipHash: b.sectorTipHash.Bytes(), }) if err != nil { - return errors.Wrap(err, "error sending commit") + return blob.ZeroHash, errors.Wrap(err, "error sending commit") } b.committed = true - return nil + return b.sectorTipHash, nil } diff --git a/rpc/server.go b/rpc/server.go index 8782170..d97d1d3 100644 --- a/rpc/server.go +++ b/rpc/server.go @@ -6,17 +6,20 @@ import ( "fnd/crypto" "fnd/log" "fnd/p2p" + "fnd/protocol" apiv1 "fnd/rpc/v1" "fnd/store" "fnd/util" "fnd/wire" - "github.com/pkg/errors" - "github.com/syndtr/goleveldb/leveldb" - "google.golang.org/grpc" + "io" "net" "strconv" "sync/atomic" "time" + + "github.com/pkg/errors" + "github.com/syndtr/goleveldb/leveldb" + "google.golang.org/grpc" ) const ( @@ -221,69 +224,57 @@ func (s *Server) Checkout(ctx context.Context, req *apiv1.CheckoutReq) (*apiv1.C if err != nil { return nil, err } + var epochHeight, sectorSize uint16 + var sectorTipHash crypto.Hash = blob.ZeroHash + header, err := store.GetHeader(s.db, req.Name) + if err != nil { + epochHeight = protocol.CurrentEpoch(req.Name) + } else { + epochHeight = header.EpochHeight + sectorSize = header.SectorSize + sectorTipHash = header.SectorTipHash + } + tx, err := bl.Transaction() if err != nil { return nil, err } + _, err = tx.Seek(int64(sectorSize)*int64(blob.SectorBytes), io.SeekStart) + if err != nil { + return nil, err + } + s.txStore.Set(strconv.FormatUint(uint64(txID), 32), &awaitingTx{ blob: bl, tx: tx, }, TransactionExpiry) return &apiv1.CheckoutRes{ - TxID: txID, + TxID: txID, + EpochHeight: uint32(epochHeight), + SectorSize: uint32(sectorSize), + SectorTipHash: sectorTipHash.Bytes(), }, nil } -func (s *Server) WriteAt(ctx context.Context, req *apiv1.WriteAtReq) (*apiv1.WriteAtRes, error) { +func (s *Server) WriteSector(ctx context.Context, req *apiv1.WriteSectorReq) (*apiv1.WriteSectorRes, error) { awaiting := s.txStore.Get(strconv.FormatUint(uint64(req.TxID), 32)).(*awaitingTx) if awaiting == nil { return nil, errors.New("transaction ID not found") } tx := awaiting.tx // we want clients to handle partial writes - n, err := tx.WriteAt(req.Data, int64(req.Offset)) - res := &apiv1.WriteAtRes{ - BytesWritten: uint32(n), - } + var sector blob.Sector + copy(sector[:], req.Data) + err := tx.WriteSector(sector) + res := &apiv1.WriteSectorRes{} if err != nil { res.WriteErr = err.Error() } return res, nil } -func (s *Server) Truncate(ctx context.Context, req *apiv1.TruncateReq) (*apiv1.Empty, error) { - awaiting := s.txStore.Get(strconv.FormatUint(uint64(req.TxID), 32)).(*awaitingTx) - if awaiting == nil { - return nil, errors.New("transaction ID not found") - } - - tx := awaiting.tx - if err := tx.Truncate(); err != nil { - return nil, errors.Wrap(err, "error truncating blob") - } - - return emptyRes, nil -} - -func (s *Server) PreCommit(ctx context.Context, req *apiv1.PreCommitReq) (*apiv1.PreCommitRes, error) { - awaiting := s.txStore.Get(strconv.FormatUint(uint64(req.TxID), 32)) - if awaiting == nil { - return nil, errors.New("transaction ID not found") - } - - tx := awaiting.(*awaitingTx).tx - mt, err := blob.Merkleize(blob.NewReader(tx)) - if err != nil { - return nil, errors.Wrap(err, "error generating blob merkle root") - } - - return &apiv1.PreCommitRes{ - MerkleRoot: mt.Root().Bytes(), - }, nil -} - func (s *Server) Commit(ctx context.Context, req *apiv1.CommitReq) (*apiv1.CommitRes, error) { id := strconv.FormatUint(uint64(req.TxID), 32) awaiting := s.txStore.Get(id).(*awaitingTx) @@ -297,15 +288,26 @@ func (s *Server) Commit(ctx context.Context, req *apiv1.CommitReq) (*apiv1.Commi if err != nil { return nil, errors.Wrap(err, "error getting name info") } - mt, err := blob.Merkleize(blob.NewReader(tx)) + + epochHeight := uint16(req.EpochHeight) + sectorSize := uint16(req.SectorSize) + sectorTipHash, err := crypto.NewHashFromBytes(req.SectorTipHash) if err != nil { - return nil, errors.Wrap(err, "error generating blob merkle root") + return nil, errors.Wrap(err, "error parsing sector tip hash") + } + + hashes, err := blob.SerialHash(blob.NewReader(tx), crypto.ZeroHash, sectorSize) + if err != nil { + return nil, errors.Wrap(err, "error getting sector hashes") + } + + if hashes.Tip() != sectorTipHash { + return nil, errors.New("sector tip hash mismatch") } var sig crypto.Signature copy(sig[:], req.Signature) - ts := time.Unix(int64(req.Timestamp), 0) - h := blob.SealHash(name, ts, mt.Root(), crypto.ZeroHash) + h := blob.SealHash(name, epochHeight, sectorSize, sectorTipHash, crypto.ZeroHash) if !crypto.VerifySigPub(info.PublicKey, sig, h) { return nil, errors.New("signature verification failed") } @@ -317,13 +319,14 @@ func (s *Server) Commit(ctx context.Context, req *apiv1.CommitReq) (*apiv1.Commi err = store.WithTx(s.db, func(tx *leveldb.Transaction) error { return store.SetHeaderTx(tx, &store.Header{ - Name: name, - Timestamp: ts, - MerkleRoot: mt.Root(), - Signature: sig, - ReservedRoot: crypto.ZeroHash, - ReceivedAt: time.Now(), - }, mt.ProtocolBase()) + Name: name, + EpochHeight: epochHeight, + SectorSize: sectorSize, + SectorTipHash: sectorTipHash, + Signature: sig, + ReservedRoot: crypto.ZeroHash, + EpochStartAt: time.Now(), + }, hashes) }) if err != nil { return nil, errors.Wrap(err, "error storing header") @@ -340,10 +343,9 @@ func (s *Server) Commit(ctx context.Context, req *apiv1.CommitReq) (*apiv1.Commi var recips []crypto.Hash if req.Broadcast { recips, _ = p2p.GossipAll(s.mux, &wire.Update{ - Name: name, - Timestamp: ts, - MerkleRoot: mt.Root(), - Signature: sig, + Name: name, + EpochHeight: epochHeight, + SectorSize: sectorSize, }) } s.lgr.Info("committed blob", "name", name, "recipient_count", len(recips)) @@ -351,6 +353,37 @@ func (s *Server) Commit(ctx context.Context, req *apiv1.CommitReq) (*apiv1.Commi return &apiv1.CommitRes{}, nil } +func (s *Server) ResetEpoch(ctx context.Context, req *apiv1.ResetEpochReq) (*apiv1.ResetEpochRes, error) { + id := strconv.FormatUint(uint64(req.TxID), 32) + awaiting := s.txStore.Get(id).(*awaitingTx) + if awaiting == nil { + return nil, errors.New("transaction ID not found") + } + + tx := awaiting.tx + name := tx.Name() + header, err := store.GetHeader(s.db, name) + if err != nil { + return nil, err + } + + // TODO: verify if epoch increments + // verify if header and ban is lifted on reset + if header.EpochHeight >= protocol.CurrentEpoch(name) { + return nil, errors.New("cannot reset epoch ahead of schedule") + } + + if err := s.bs.Reset(name); err != nil { + return nil, err + } + + if err := store.TruncateHeaderName(s.db, name); err != nil { + return nil, err + } + + return &apiv1.ResetEpochRes{}, nil +} + func (s *Server) ReadAt(_ context.Context, req *apiv1.ReadAtReq) (*apiv1.ReadAtRes, error) { if req.Offset > blob.Size { return nil, errors.New("offset is beyond blob bounds") @@ -396,15 +429,15 @@ func (s *Server) GetBlobInfo(_ context.Context, req *apiv1.BlobInfoReq) (*apiv1. } return &apiv1.BlobInfoRes{ - Name: name, - PublicKey: info.PublicKey.SerializeCompressed(), - ImportHeight: uint32(info.ImportHeight), - Timestamp: uint64(header.Timestamp.Unix()), - MerkleRoot: header.MerkleRoot[:], - ReservedRoot: header.ReservedRoot[:], - ReceivedAt: uint64(header.ReceivedAt.Unix()), - Signature: header.Signature[:], - Timebank: uint32(header.Timebank), + Name: name, + PublicKey: info.PublicKey.SerializeCompressed(), + ImportHeight: uint32(info.ImportHeight), + EpochHeight: uint32(header.EpochHeight), + SectorSize: uint32(header.SectorSize), + SectorTipHash: header.SectorTipHash[:], + ReservedRoot: header.ReservedRoot[:], + ReceivedAt: uint64(header.EpochStartAt.Unix()), + Signature: header.Signature[:], }, nil } @@ -424,15 +457,16 @@ func (s *Server) ListBlobInfo(req *apiv1.ListBlobInfoReq, srv apiv1.Footnotev1_L return nil } res := &apiv1.BlobInfoRes{ - Name: info.Name, - PublicKey: info.PublicKey.SerializeCompressed(), - ImportHeight: uint32(info.ImportHeight), - Timestamp: uint64(info.Timestamp.Unix()), - MerkleRoot: info.MerkleRoot[:], - ReservedRoot: info.ReservedRoot[:], - ReceivedAt: uint64(info.ReceivedAt.Unix()), - Signature: info.Signature[:], - Timebank: uint32(info.Timebank), + Name: info.Name, + PublicKey: info.PublicKey.SerializeCompressed(), + ImportHeight: uint32(info.ImportHeight), + EpochHeight: uint32(info.EpochHeight), + SectorSize: uint32(info.SectorSize), + SectorTipHash: info.SectorTipHash[:], + ReservedRoot: info.ReservedRoot[:], + Signature: info.Signature[:], + ReceivedAt: uint64(info.ReceivedAt.Unix()), + BannedAt: uint64(info.BannedAt.Unix()), } if err = srv.Send(res); err != nil { return errors.Wrap(err, "error sending info") @@ -447,10 +481,9 @@ func (s *Server) SendUpdate(_ context.Context, req *apiv1.SendUpdateReq) (*apiv1 } recips, _ := p2p.GossipAll(s.mux, &wire.Update{ - Name: req.Name, - Timestamp: header.Timestamp, - MerkleRoot: header.MerkleRoot, - Signature: header.Signature, + Name: req.Name, + EpochHeight: header.EpochHeight, + SectorSize: header.SectorSize, }) return &apiv1.SendUpdateRes{ diff --git a/rpc/v1/api.pb.go b/rpc/v1/api.pb.go index f697a73..2e7fba4 100644 --- a/rpc/v1/api.pb.go +++ b/rpc/v1/api.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.24.0 -// protoc v3.11.4 +// protoc-gen-go v1.23.0 +// protoc v3.12.4 // source: api.proto package v1 @@ -606,7 +606,10 @@ type CheckoutRes struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TxID uint32 `protobuf:"varint,1,opt,name=txID,proto3" json:"txID,omitempty"` + TxID uint32 `protobuf:"varint,1,opt,name=txID,proto3" json:"txID,omitempty"` + EpochHeight uint32 `protobuf:"varint,2,opt,name=epochHeight,proto3" json:"epochHeight,omitempty"` + SectorSize uint32 `protobuf:"varint,3,opt,name=sectorSize,proto3" json:"sectorSize,omitempty"` + SectorTipHash []byte `protobuf:"bytes,4,opt,name=sectorTipHash,proto3" json:"sectorTipHash,omitempty"` } func (x *CheckoutRes) Reset() { @@ -648,149 +651,53 @@ func (x *CheckoutRes) GetTxID() uint32 { return 0 } -type WriteAtReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - TxID uint32 `protobuf:"varint,1,opt,name=txID,proto3" json:"txID,omitempty"` - Offset uint32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` - Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` -} - -func (x *WriteAtReq) Reset() { - *x = WriteAtReq{} - if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WriteAtReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WriteAtReq) ProtoMessage() {} - -func (x *WriteAtReq) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WriteAtReq.ProtoReflect.Descriptor instead. -func (*WriteAtReq) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{11} -} - -func (x *WriteAtReq) GetTxID() uint32 { +func (x *CheckoutRes) GetEpochHeight() uint32 { if x != nil { - return x.TxID + return x.EpochHeight } return 0 } -func (x *WriteAtReq) GetOffset() uint32 { +func (x *CheckoutRes) GetSectorSize() uint32 { if x != nil { - return x.Offset + return x.SectorSize } return 0 } -func (x *WriteAtReq) GetData() []byte { +func (x *CheckoutRes) GetSectorTipHash() []byte { if x != nil { - return x.Data + return x.SectorTipHash } return nil } -type WriteAtRes struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - BytesWritten uint32 `protobuf:"varint,1,opt,name=bytesWritten,proto3" json:"bytesWritten,omitempty"` - WriteErr string `protobuf:"bytes,2,opt,name=writeErr,proto3" json:"writeErr,omitempty"` -} - -func (x *WriteAtRes) Reset() { - *x = WriteAtRes{} - if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *WriteAtRes) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WriteAtRes) ProtoMessage() {} - -func (x *WriteAtRes) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WriteAtRes.ProtoReflect.Descriptor instead. -func (*WriteAtRes) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{12} -} - -func (x *WriteAtRes) GetBytesWritten() uint32 { - if x != nil { - return x.BytesWritten - } - return 0 -} - -func (x *WriteAtRes) GetWriteErr() string { - if x != nil { - return x.WriteErr - } - return "" -} - -type TruncateReq struct { +type WriteSectorReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields TxID uint32 `protobuf:"varint,1,opt,name=txID,proto3" json:"txID,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` } -func (x *TruncateReq) Reset() { - *x = TruncateReq{} +func (x *WriteSectorReq) Reset() { + *x = WriteSectorReq{} if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[13] + mi := &file_api_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *TruncateReq) String() string { +func (x *WriteSectorReq) String() string { return protoimpl.X.MessageStringOf(x) } -func (*TruncateReq) ProtoMessage() {} +func (*WriteSectorReq) ProtoMessage() {} -func (x *TruncateReq) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[13] +func (x *WriteSectorReq) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -801,128 +708,50 @@ func (x *TruncateReq) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use TruncateReq.ProtoReflect.Descriptor instead. -func (*TruncateReq) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{13} +// Deprecated: Use WriteSectorReq.ProtoReflect.Descriptor instead. +func (*WriteSectorReq) Descriptor() ([]byte, []int) { + return file_api_proto_rawDescGZIP(), []int{11} } -func (x *TruncateReq) GetTxID() uint32 { +func (x *WriteSectorReq) GetTxID() uint32 { if x != nil { return x.TxID } return 0 } -type TruncateRes struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *TruncateRes) Reset() { - *x = TruncateRes{} - if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TruncateRes) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TruncateRes) ProtoMessage() {} - -func (x *TruncateRes) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TruncateRes.ProtoReflect.Descriptor instead. -func (*TruncateRes) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{14} -} - -type PreCommitReq struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - TxID uint32 `protobuf:"varint,1,opt,name=txID,proto3" json:"txID,omitempty"` -} - -func (x *PreCommitReq) Reset() { - *x = PreCommitReq{} - if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *PreCommitReq) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PreCommitReq) ProtoMessage() {} - -func (x *PreCommitReq) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[15] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PreCommitReq.ProtoReflect.Descriptor instead. -func (*PreCommitReq) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{15} -} - -func (x *PreCommitReq) GetTxID() uint32 { +func (x *WriteSectorReq) GetData() []byte { if x != nil { - return x.TxID + return x.Data } - return 0 + return nil } -type PreCommitRes struct { +type WriteSectorRes struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - MerkleRoot []byte `protobuf:"bytes,1,opt,name=merkleRoot,proto3" json:"merkleRoot,omitempty"` + WriteErr string `protobuf:"bytes,1,opt,name=writeErr,proto3" json:"writeErr,omitempty"` } -func (x *PreCommitRes) Reset() { - *x = PreCommitRes{} +func (x *WriteSectorRes) Reset() { + *x = WriteSectorRes{} if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[16] + mi := &file_api_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *PreCommitRes) String() string { +func (x *WriteSectorRes) String() string { return protoimpl.X.MessageStringOf(x) } -func (*PreCommitRes) ProtoMessage() {} +func (*WriteSectorRes) ProtoMessage() {} -func (x *PreCommitRes) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[16] +func (x *WriteSectorRes) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -933,16 +762,16 @@ func (x *PreCommitRes) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use PreCommitRes.ProtoReflect.Descriptor instead. -func (*PreCommitRes) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{16} +// Deprecated: Use WriteSectorRes.ProtoReflect.Descriptor instead. +func (*WriteSectorRes) Descriptor() ([]byte, []int) { + return file_api_proto_rawDescGZIP(), []int{12} } -func (x *PreCommitRes) GetMerkleRoot() []byte { +func (x *WriteSectorRes) GetWriteErr() string { if x != nil { - return x.MerkleRoot + return x.WriteErr } - return nil + return "" } type CommitReq struct { @@ -950,16 +779,19 @@ type CommitReq struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - TxID uint32 `protobuf:"varint,1,opt,name=txID,proto3" json:"txID,omitempty"` - Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` - Broadcast bool `protobuf:"varint,4,opt,name=broadcast,proto3" json:"broadcast,omitempty"` + TxID uint32 `protobuf:"varint,1,opt,name=txID,proto3" json:"txID,omitempty"` + Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Signature []byte `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` + Broadcast bool `protobuf:"varint,4,opt,name=broadcast,proto3" json:"broadcast,omitempty"` + EpochHeight uint32 `protobuf:"varint,5,opt,name=epochHeight,proto3" json:"epochHeight,omitempty"` + SectorSize uint32 `protobuf:"varint,6,opt,name=sectorSize,proto3" json:"sectorSize,omitempty"` + SectorTipHash []byte `protobuf:"bytes,7,opt,name=sectorTipHash,proto3" json:"sectorTipHash,omitempty"` } func (x *CommitReq) Reset() { *x = CommitReq{} if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[17] + mi := &file_api_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -972,7 +804,7 @@ func (x *CommitReq) String() string { func (*CommitReq) ProtoMessage() {} func (x *CommitReq) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[17] + mi := &file_api_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -985,7 +817,7 @@ func (x *CommitReq) ProtoReflect() protoreflect.Message { // Deprecated: Use CommitReq.ProtoReflect.Descriptor instead. func (*CommitReq) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{17} + return file_api_proto_rawDescGZIP(), []int{13} } func (x *CommitReq) GetTxID() uint32 { @@ -1016,6 +848,27 @@ func (x *CommitReq) GetBroadcast() bool { return false } +func (x *CommitReq) GetEpochHeight() uint32 { + if x != nil { + return x.EpochHeight + } + return 0 +} + +func (x *CommitReq) GetSectorSize() uint32 { + if x != nil { + return x.SectorSize + } + return 0 +} + +func (x *CommitReq) GetSectorTipHash() []byte { + if x != nil { + return x.SectorTipHash + } + return nil +} + type CommitRes struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1025,7 +878,7 @@ type CommitRes struct { func (x *CommitRes) Reset() { *x = CommitRes{} if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[18] + mi := &file_api_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1038,7 +891,7 @@ func (x *CommitRes) String() string { func (*CommitRes) ProtoMessage() {} func (x *CommitRes) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[18] + mi := &file_api_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1051,7 +904,7 @@ func (x *CommitRes) ProtoReflect() protoreflect.Message { // Deprecated: Use CommitRes.ProtoReflect.Descriptor instead. func (*CommitRes) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{18} + return file_api_proto_rawDescGZIP(), []int{14} } type ReadAtReq struct { @@ -1067,7 +920,7 @@ type ReadAtReq struct { func (x *ReadAtReq) Reset() { *x = ReadAtReq{} if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[19] + mi := &file_api_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1080,7 +933,7 @@ func (x *ReadAtReq) String() string { func (*ReadAtReq) ProtoMessage() {} func (x *ReadAtReq) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[19] + mi := &file_api_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1093,7 +946,7 @@ func (x *ReadAtReq) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadAtReq.ProtoReflect.Descriptor instead. func (*ReadAtReq) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{19} + return file_api_proto_rawDescGZIP(), []int{15} } func (x *ReadAtReq) GetName() string { @@ -1129,7 +982,7 @@ type ReadAtRes struct { func (x *ReadAtRes) Reset() { *x = ReadAtRes{} if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[20] + mi := &file_api_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1142,7 +995,7 @@ func (x *ReadAtRes) String() string { func (*ReadAtRes) ProtoMessage() {} func (x *ReadAtRes) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[20] + mi := &file_api_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1155,7 +1008,7 @@ func (x *ReadAtRes) ProtoReflect() protoreflect.Message { // Deprecated: Use ReadAtRes.ProtoReflect.Descriptor instead. func (*ReadAtRes) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{20} + return file_api_proto_rawDescGZIP(), []int{16} } func (x *ReadAtRes) GetOffset() uint32 { @@ -1172,6 +1025,91 @@ func (x *ReadAtRes) GetData() []byte { return nil } +type ResetEpochReq struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TxID uint32 `protobuf:"varint,1,opt,name=txID,proto3" json:"txID,omitempty"` +} + +func (x *ResetEpochReq) Reset() { + *x = ResetEpochReq{} + if protoimpl.UnsafeEnabled { + mi := &file_api_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResetEpochReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResetEpochReq) ProtoMessage() {} + +func (x *ResetEpochReq) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResetEpochReq.ProtoReflect.Descriptor instead. +func (*ResetEpochReq) Descriptor() ([]byte, []int) { + return file_api_proto_rawDescGZIP(), []int{17} +} + +func (x *ResetEpochReq) GetTxID() uint32 { + if x != nil { + return x.TxID + } + return 0 +} + +type ResetEpochRes struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *ResetEpochRes) Reset() { + *x = ResetEpochRes{} + if protoimpl.UnsafeEnabled { + mi := &file_api_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResetEpochRes) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResetEpochRes) ProtoMessage() {} + +func (x *ResetEpochRes) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ResetEpochRes.ProtoReflect.Descriptor instead. +func (*ResetEpochRes) Descriptor() ([]byte, []int) { + return file_api_proto_rawDescGZIP(), []int{18} +} + type BlobInfoReq struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1183,7 +1121,7 @@ type BlobInfoReq struct { func (x *BlobInfoReq) Reset() { *x = BlobInfoReq{} if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[21] + mi := &file_api_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1196,7 +1134,7 @@ func (x *BlobInfoReq) String() string { func (*BlobInfoReq) ProtoMessage() {} func (x *BlobInfoReq) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[21] + mi := &file_api_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1209,7 +1147,7 @@ func (x *BlobInfoReq) ProtoReflect() protoreflect.Message { // Deprecated: Use BlobInfoReq.ProtoReflect.Descriptor instead. func (*BlobInfoReq) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{21} + return file_api_proto_rawDescGZIP(), []int{19} } func (x *BlobInfoReq) GetName() string { @@ -1230,7 +1168,7 @@ type ListBlobInfoReq struct { func (x *ListBlobInfoReq) Reset() { *x = ListBlobInfoReq{} if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[22] + mi := &file_api_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1243,7 +1181,7 @@ func (x *ListBlobInfoReq) String() string { func (*ListBlobInfoReq) ProtoMessage() {} func (x *ListBlobInfoReq) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[22] + mi := &file_api_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1256,7 +1194,7 @@ func (x *ListBlobInfoReq) ProtoReflect() protoreflect.Message { // Deprecated: Use ListBlobInfoReq.ProtoReflect.Descriptor instead. func (*ListBlobInfoReq) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{22} + return file_api_proto_rawDescGZIP(), []int{20} } func (x *ListBlobInfoReq) GetStart() string { @@ -1271,21 +1209,22 @@ type BlobInfoRes struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - PublicKey []byte `protobuf:"bytes,2,opt,name=publicKey,proto3" json:"publicKey,omitempty"` - ImportHeight uint32 `protobuf:"varint,3,opt,name=importHeight,proto3" json:"importHeight,omitempty"` - Timestamp uint64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - MerkleRoot []byte `protobuf:"bytes,5,opt,name=merkleRoot,proto3" json:"merkleRoot,omitempty"` - ReservedRoot []byte `protobuf:"bytes,6,opt,name=reservedRoot,proto3" json:"reservedRoot,omitempty"` - ReceivedAt uint64 `protobuf:"varint,7,opt,name=receivedAt,proto3" json:"receivedAt,omitempty"` - Signature []byte `protobuf:"bytes,8,opt,name=signature,proto3" json:"signature,omitempty"` - Timebank uint32 `protobuf:"varint,9,opt,name=timebank,proto3" json:"timebank,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + PublicKey []byte `protobuf:"bytes,2,opt,name=publicKey,proto3" json:"publicKey,omitempty"` + ImportHeight uint32 `protobuf:"varint,3,opt,name=importHeight,proto3" json:"importHeight,omitempty"` + EpochHeight uint32 `protobuf:"varint,4,opt,name=epochHeight,proto3" json:"epochHeight,omitempty"` // protobuf doesn't have uint16 + SectorSize uint32 `protobuf:"varint,5,opt,name=sectorSize,proto3" json:"sectorSize,omitempty"` // ditto ^ + SectorTipHash []byte `protobuf:"bytes,6,opt,name=sectorTipHash,proto3" json:"sectorTipHash,omitempty"` + ReservedRoot []byte `protobuf:"bytes,7,opt,name=reservedRoot,proto3" json:"reservedRoot,omitempty"` + Signature []byte `protobuf:"bytes,8,opt,name=signature,proto3" json:"signature,omitempty"` + ReceivedAt uint64 `protobuf:"varint,9,opt,name=receivedAt,proto3" json:"receivedAt,omitempty"` + BannedAt uint64 `protobuf:"varint,10,opt,name=bannedAt,proto3" json:"bannedAt,omitempty"` } func (x *BlobInfoRes) Reset() { *x = BlobInfoRes{} if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[23] + mi := &file_api_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1298,7 +1237,7 @@ func (x *BlobInfoRes) String() string { func (*BlobInfoRes) ProtoMessage() {} func (x *BlobInfoRes) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[23] + mi := &file_api_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1311,7 +1250,7 @@ func (x *BlobInfoRes) ProtoReflect() protoreflect.Message { // Deprecated: Use BlobInfoRes.ProtoReflect.Descriptor instead. func (*BlobInfoRes) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{23} + return file_api_proto_rawDescGZIP(), []int{21} } func (x *BlobInfoRes) GetName() string { @@ -1335,32 +1274,32 @@ func (x *BlobInfoRes) GetImportHeight() uint32 { return 0 } -func (x *BlobInfoRes) GetTimestamp() uint64 { +func (x *BlobInfoRes) GetEpochHeight() uint32 { if x != nil { - return x.Timestamp + return x.EpochHeight } return 0 } -func (x *BlobInfoRes) GetMerkleRoot() []byte { +func (x *BlobInfoRes) GetSectorSize() uint32 { if x != nil { - return x.MerkleRoot + return x.SectorSize } - return nil + return 0 } -func (x *BlobInfoRes) GetReservedRoot() []byte { +func (x *BlobInfoRes) GetSectorTipHash() []byte { if x != nil { - return x.ReservedRoot + return x.SectorTipHash } return nil } -func (x *BlobInfoRes) GetReceivedAt() uint64 { +func (x *BlobInfoRes) GetReservedRoot() []byte { if x != nil { - return x.ReceivedAt + return x.ReservedRoot } - return 0 + return nil } func (x *BlobInfoRes) GetSignature() []byte { @@ -1370,9 +1309,16 @@ func (x *BlobInfoRes) GetSignature() []byte { return nil } -func (x *BlobInfoRes) GetTimebank() uint32 { +func (x *BlobInfoRes) GetReceivedAt() uint64 { if x != nil { - return x.Timebank + return x.ReceivedAt + } + return 0 +} + +func (x *BlobInfoRes) GetBannedAt() uint64 { + if x != nil { + return x.BannedAt } return 0 } @@ -1388,7 +1334,7 @@ type SendUpdateReq struct { func (x *SendUpdateReq) Reset() { *x = SendUpdateReq{} if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[24] + mi := &file_api_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1401,7 +1347,7 @@ func (x *SendUpdateReq) String() string { func (*SendUpdateReq) ProtoMessage() {} func (x *SendUpdateReq) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[24] + mi := &file_api_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1414,7 +1360,7 @@ func (x *SendUpdateReq) ProtoReflect() protoreflect.Message { // Deprecated: Use SendUpdateReq.ProtoReflect.Descriptor instead. func (*SendUpdateReq) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{24} + return file_api_proto_rawDescGZIP(), []int{22} } func (x *SendUpdateReq) GetName() string { @@ -1435,7 +1381,7 @@ type SendUpdateRes struct { func (x *SendUpdateRes) Reset() { *x = SendUpdateRes{} if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[25] + mi := &file_api_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1448,7 +1394,7 @@ func (x *SendUpdateRes) String() string { func (*SendUpdateRes) ProtoMessage() {} func (x *SendUpdateRes) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[25] + mi := &file_api_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1461,7 +1407,7 @@ func (x *SendUpdateRes) ProtoReflect() protoreflect.Message { // Deprecated: Use SendUpdateRes.ProtoReflect.Descriptor instead. func (*SendUpdateRes) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{25} + return file_api_proto_rawDescGZIP(), []int{23} } func (x *SendUpdateRes) GetRecipientCount() uint32 { @@ -1519,109 +1465,115 @@ var file_api_proto_rawDesc = []byte{ 0x65, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x77, 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x21, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x21, 0x0a, - 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x78, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x78, 0x49, 0x44, - 0x22, 0x4c, 0x0a, 0x0a, 0x57, 0x72, 0x69, 0x74, 0x65, 0x41, 0x74, 0x52, 0x65, 0x71, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x78, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x78, - 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x4c, - 0x0a, 0x0a, 0x57, 0x72, 0x69, 0x74, 0x65, 0x41, 0x74, 0x52, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0c, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x57, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0c, 0x62, 0x79, 0x74, 0x65, 0x73, 0x57, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, - 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x72, 0x69, 0x74, 0x65, 0x45, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x77, 0x72, 0x69, 0x74, 0x65, 0x45, 0x72, 0x72, 0x22, 0x21, 0x0a, 0x0b, - 0x54, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x74, - 0x78, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x78, 0x49, 0x44, 0x22, - 0x0d, 0x0a, 0x0b, 0x54, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x22, 0x22, - 0x0a, 0x0c, 0x50, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x78, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x78, - 0x49, 0x44, 0x22, 0x2e, 0x0a, 0x0c, 0x50, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, - 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, - 0x6f, 0x74, 0x22, 0x79, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x12, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x89, 0x01, + 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x78, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x78, 0x49, + 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x69, 0x7a, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, + 0x69, 0x7a, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, 0x69, 0x70, + 0x48, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x22, 0x38, 0x0a, 0x0e, 0x57, 0x72, 0x69, + 0x74, 0x65, 0x53, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x78, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x78, 0x49, 0x44, 0x12, + 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x22, 0x2c, 0x0a, 0x0e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x53, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x52, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x72, 0x69, 0x74, 0x65, 0x45, 0x72, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x72, 0x69, 0x74, 0x65, 0x45, 0x72, + 0x72, 0x22, 0xe1, 0x01, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x78, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x22, 0x0b, 0x0a, - 0x09, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x22, 0x49, 0x0a, 0x09, 0x52, 0x65, - 0x61, 0x64, 0x41, 0x74, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, - 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, - 0x73, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6c, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x03, 0x6c, 0x65, 0x6e, 0x22, 0x37, 0x0a, 0x09, 0x52, 0x65, 0x61, 0x64, 0x41, 0x74, 0x52, - 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x21, - 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x22, 0x27, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x22, 0x9f, 0x02, 0x0a, 0x0b, 0x42, - 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, - 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x0c, - 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0c, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1e, - 0x0a, 0x0a, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0a, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x22, - 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x6f, - 0x6f, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x41, 0x74, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, - 0x41, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x62, 0x61, 0x6e, 0x6b, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x62, 0x61, 0x6e, 0x6b, 0x22, 0x23, 0x0a, 0x0d, - 0x53, 0x65, 0x6e, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x22, 0x37, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, 0x65, 0x63, 0x69, - 0x70, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xaa, 0x04, 0x0a, 0x0a, 0x46, - 0x6f, 0x6f, 0x74, 0x6e, 0x6f, 0x74, 0x65, 0x76, 0x31, 0x12, 0x22, 0x0a, 0x09, 0x47, 0x65, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, - 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x12, 0x1e, 0x0a, - 0x07, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x12, 0x0b, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x65, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x06, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x1e, 0x0a, - 0x07, 0x42, 0x61, 0x6e, 0x50, 0x65, 0x65, 0x72, 0x12, 0x0b, 0x2e, 0x42, 0x61, 0x6e, 0x50, 0x65, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x06, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x22, 0x0a, - 0x09, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x50, 0x65, 0x65, 0x72, 0x12, 0x0d, 0x2e, 0x55, 0x6e, 0x62, - 0x61, 0x6e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x06, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x12, 0x2b, 0x0a, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x0d, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x0d, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x30, 0x01, 0x12, 0x26, - 0x0a, 0x08, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x6f, 0x75, 0x74, 0x12, 0x0c, 0x2e, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x6f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x07, 0x57, 0x72, 0x69, 0x74, 0x65, 0x41, - 0x74, 0x12, 0x0b, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x41, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x0b, - 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x41, 0x74, 0x52, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x08, 0x54, - 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x12, 0x0c, 0x2e, 0x54, 0x72, 0x75, 0x6e, 0x63, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x06, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x29, 0x0a, - 0x09, 0x50, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x0d, 0x2e, 0x50, 0x72, 0x65, - 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x0d, 0x2e, 0x50, 0x72, 0x65, 0x43, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x06, 0x43, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x12, 0x0a, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x0a, - 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x06, 0x52, 0x65, - 0x61, 0x64, 0x41, 0x74, 0x12, 0x0a, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x41, 0x74, 0x52, 0x65, 0x71, - 0x1a, 0x0a, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x41, 0x74, 0x52, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x0b, - 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0c, 0x2e, 0x42, 0x6c, - 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x42, 0x6c, 0x6f, 0x62, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x42, - 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x6c, - 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x42, 0x6c, 0x6f, 0x62, - 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x30, 0x01, 0x12, 0x2c, 0x0a, 0x0a, 0x53, 0x65, 0x6e, - 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0e, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x42, 0x04, 0x5a, 0x02, 0x76, 0x31, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x28, 0x08, 0x52, 0x09, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x20, 0x0a, + 0x0b, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0b, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, + 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x12, + 0x24, 0x0a, 0x0d, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, 0x69, + 0x70, 0x48, 0x61, 0x73, 0x68, 0x22, 0x0b, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, + 0x65, 0x73, 0x22, 0x49, 0x0a, 0x09, 0x52, 0x65, 0x61, 0x64, 0x41, 0x74, 0x52, 0x65, 0x71, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6c, + 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6c, 0x65, 0x6e, 0x22, 0x37, 0x0a, + 0x09, 0x52, 0x65, 0x61, 0x64, 0x41, 0x74, 0x52, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x23, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x65, 0x74, 0x45, + 0x70, 0x6f, 0x63, 0x68, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x78, 0x49, 0x44, 0x22, 0x0f, 0x0a, 0x0d, 0x52, + 0x65, 0x73, 0x65, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x65, 0x73, 0x22, 0x21, 0x0a, 0x0b, + 0x42, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, + 0x27, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x22, 0xc9, 0x02, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, + 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6d, + 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0c, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x20, + 0x0a, 0x0b, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x69, 0x7a, 0x65, + 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, 0x69, 0x70, 0x48, 0x61, 0x73, + 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x54, + 0x69, 0x70, 0x48, 0x61, 0x73, 0x68, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x64, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, 0x65, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x65, + 0x69, 0x76, 0x65, 0x64, 0x41, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x72, 0x65, + 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x61, 0x6e, 0x6e, + 0x65, 0x64, 0x41, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x62, 0x61, 0x6e, 0x6e, + 0x65, 0x64, 0x41, 0x74, 0x22, 0x23, 0x0a, 0x0d, 0x53, 0x65, 0x6e, 0x64, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x37, 0x0a, 0x0d, 0x53, 0x65, 0x6e, + 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, + 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0e, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x32, 0x97, 0x04, 0x0a, 0x0a, 0x46, 0x6f, 0x6f, 0x74, 0x6e, 0x6f, 0x74, 0x65, 0x76, + 0x31, 0x12, 0x22, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, + 0x12, 0x0b, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x06, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x1e, 0x0a, 0x07, 0x42, 0x61, 0x6e, 0x50, 0x65, 0x65, 0x72, + 0x12, 0x0b, 0x2e, 0x42, 0x61, 0x6e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x06, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x22, 0x0a, 0x09, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x50, 0x65, + 0x65, 0x72, 0x12, 0x0d, 0x2e, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x1a, 0x06, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2b, 0x0a, 0x09, 0x4c, 0x69, 0x73, + 0x74, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x0d, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, + 0x72, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x0d, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x65, 0x65, 0x72, + 0x73, 0x52, 0x65, 0x73, 0x30, 0x01, 0x12, 0x26, 0x0a, 0x08, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x6f, + 0x75, 0x74, 0x12, 0x0c, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, + 0x1a, 0x0c, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x12, 0x2f, + 0x0a, 0x0b, 0x57, 0x72, 0x69, 0x74, 0x65, 0x53, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x0f, 0x2e, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x53, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x0f, + 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x53, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x12, + 0x20, 0x0a, 0x06, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x0a, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x0a, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x52, 0x65, + 0x73, 0x12, 0x2c, 0x0a, 0x0a, 0x52, 0x65, 0x73, 0x65, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x12, + 0x0e, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x65, 0x71, 0x1a, + 0x0e, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x65, 0x73, 0x12, + 0x20, 0x0a, 0x06, 0x52, 0x65, 0x61, 0x64, 0x41, 0x74, 0x12, 0x0a, 0x2e, 0x52, 0x65, 0x61, 0x64, + 0x41, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x0a, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x41, 0x74, 0x52, 0x65, + 0x73, 0x12, 0x29, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x0c, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x0c, + 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x0c, + 0x4c, 0x69, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x10, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x0c, + 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x30, 0x01, 0x12, 0x2c, + 0x0a, 0x0a, 0x53, 0x65, 0x6e, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x2e, 0x53, + 0x65, 0x6e, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0e, 0x2e, 0x53, + 0x65, 0x6e, 0x64, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x42, 0x04, 0x5a, 0x02, + 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1636,7 +1588,7 @@ func file_api_proto_rawDescGZIP() []byte { return file_api_proto_rawDescData } -var file_api_proto_msgTypes = make([]protoimpl.MessageInfo, 26) +var file_api_proto_msgTypes = make([]protoimpl.MessageInfo, 24) var file_api_proto_goTypes = []interface{}{ (*Empty)(nil), // 0: Empty (*GetStatusRes)(nil), // 1: GetStatusRes @@ -1649,21 +1601,19 @@ var file_api_proto_goTypes = []interface{}{ (*ListPeersRes)(nil), // 8: ListPeersRes (*CheckoutReq)(nil), // 9: CheckoutReq (*CheckoutRes)(nil), // 10: CheckoutRes - (*WriteAtReq)(nil), // 11: WriteAtReq - (*WriteAtRes)(nil), // 12: WriteAtRes - (*TruncateReq)(nil), // 13: TruncateReq - (*TruncateRes)(nil), // 14: TruncateRes - (*PreCommitReq)(nil), // 15: PreCommitReq - (*PreCommitRes)(nil), // 16: PreCommitRes - (*CommitReq)(nil), // 17: CommitReq - (*CommitRes)(nil), // 18: CommitRes - (*ReadAtReq)(nil), // 19: ReadAtReq - (*ReadAtRes)(nil), // 20: ReadAtRes - (*BlobInfoReq)(nil), // 21: BlobInfoReq - (*ListBlobInfoReq)(nil), // 22: ListBlobInfoReq - (*BlobInfoRes)(nil), // 23: BlobInfoRes - (*SendUpdateReq)(nil), // 24: SendUpdateReq - (*SendUpdateRes)(nil), // 25: SendUpdateRes + (*WriteSectorReq)(nil), // 11: WriteSectorReq + (*WriteSectorRes)(nil), // 12: WriteSectorRes + (*CommitReq)(nil), // 13: CommitReq + (*CommitRes)(nil), // 14: CommitRes + (*ReadAtReq)(nil), // 15: ReadAtReq + (*ReadAtRes)(nil), // 16: ReadAtRes + (*ResetEpochReq)(nil), // 17: ResetEpochReq + (*ResetEpochRes)(nil), // 18: ResetEpochRes + (*BlobInfoReq)(nil), // 19: BlobInfoReq + (*ListBlobInfoReq)(nil), // 20: ListBlobInfoReq + (*BlobInfoRes)(nil), // 21: BlobInfoRes + (*SendUpdateReq)(nil), // 22: SendUpdateReq + (*SendUpdateRes)(nil), // 23: SendUpdateRes } var file_api_proto_depIdxs = []int32{ 0, // 0: Footnotev1.GetStatus:input_type -> Empty @@ -1672,30 +1622,28 @@ var file_api_proto_depIdxs = []int32{ 6, // 3: Footnotev1.UnbanPeer:input_type -> UnbanPeerReq 7, // 4: Footnotev1.ListPeers:input_type -> ListPeersReq 9, // 5: Footnotev1.Checkout:input_type -> CheckoutReq - 11, // 6: Footnotev1.WriteAt:input_type -> WriteAtReq - 13, // 7: Footnotev1.Truncate:input_type -> TruncateReq - 15, // 8: Footnotev1.PreCommit:input_type -> PreCommitReq - 17, // 9: Footnotev1.Commit:input_type -> CommitReq - 19, // 10: Footnotev1.ReadAt:input_type -> ReadAtReq - 21, // 11: Footnotev1.GetBlobInfo:input_type -> BlobInfoReq - 22, // 12: Footnotev1.ListBlobInfo:input_type -> ListBlobInfoReq - 24, // 13: Footnotev1.SendUpdate:input_type -> SendUpdateReq - 1, // 14: Footnotev1.GetStatus:output_type -> GetStatusRes - 0, // 15: Footnotev1.AddPeer:output_type -> Empty - 0, // 16: Footnotev1.BanPeer:output_type -> Empty - 0, // 17: Footnotev1.UnbanPeer:output_type -> Empty - 8, // 18: Footnotev1.ListPeers:output_type -> ListPeersRes - 10, // 19: Footnotev1.Checkout:output_type -> CheckoutRes - 12, // 20: Footnotev1.WriteAt:output_type -> WriteAtRes - 0, // 21: Footnotev1.Truncate:output_type -> Empty - 16, // 22: Footnotev1.PreCommit:output_type -> PreCommitRes - 18, // 23: Footnotev1.Commit:output_type -> CommitRes - 20, // 24: Footnotev1.ReadAt:output_type -> ReadAtRes - 23, // 25: Footnotev1.GetBlobInfo:output_type -> BlobInfoRes - 23, // 26: Footnotev1.ListBlobInfo:output_type -> BlobInfoRes - 25, // 27: Footnotev1.SendUpdate:output_type -> SendUpdateRes - 14, // [14:28] is the sub-list for method output_type - 0, // [0:14] is the sub-list for method input_type + 11, // 6: Footnotev1.WriteSector:input_type -> WriteSectorReq + 13, // 7: Footnotev1.Commit:input_type -> CommitReq + 17, // 8: Footnotev1.ResetEpoch:input_type -> ResetEpochReq + 15, // 9: Footnotev1.ReadAt:input_type -> ReadAtReq + 19, // 10: Footnotev1.GetBlobInfo:input_type -> BlobInfoReq + 20, // 11: Footnotev1.ListBlobInfo:input_type -> ListBlobInfoReq + 22, // 12: Footnotev1.SendUpdate:input_type -> SendUpdateReq + 1, // 13: Footnotev1.GetStatus:output_type -> GetStatusRes + 0, // 14: Footnotev1.AddPeer:output_type -> Empty + 0, // 15: Footnotev1.BanPeer:output_type -> Empty + 0, // 16: Footnotev1.UnbanPeer:output_type -> Empty + 8, // 17: Footnotev1.ListPeers:output_type -> ListPeersRes + 10, // 18: Footnotev1.Checkout:output_type -> CheckoutRes + 12, // 19: Footnotev1.WriteSector:output_type -> WriteSectorRes + 14, // 20: Footnotev1.Commit:output_type -> CommitRes + 18, // 21: Footnotev1.ResetEpoch:output_type -> ResetEpochRes + 16, // 22: Footnotev1.ReadAt:output_type -> ReadAtRes + 21, // 23: Footnotev1.GetBlobInfo:output_type -> BlobInfoRes + 21, // 24: Footnotev1.ListBlobInfo:output_type -> BlobInfoRes + 23, // 25: Footnotev1.SendUpdate:output_type -> SendUpdateRes + 13, // [13:26] is the sub-list for method output_type + 0, // [0:13] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -1840,7 +1788,7 @@ func file_api_proto_init() { } } file_api_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WriteAtReq); i { + switch v := v.(*WriteSectorReq); i { case 0: return &v.state case 1: @@ -1852,7 +1800,7 @@ func file_api_proto_init() { } } file_api_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*WriteAtRes); i { + switch v := v.(*WriteSectorRes); i { case 0: return &v.state case 1: @@ -1864,7 +1812,7 @@ func file_api_proto_init() { } } file_api_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TruncateReq); i { + switch v := v.(*CommitReq); i { case 0: return &v.state case 1: @@ -1876,7 +1824,7 @@ func file_api_proto_init() { } } file_api_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TruncateRes); i { + switch v := v.(*CommitRes); i { case 0: return &v.state case 1: @@ -1888,7 +1836,7 @@ func file_api_proto_init() { } } file_api_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PreCommitReq); i { + switch v := v.(*ReadAtReq); i { case 0: return &v.state case 1: @@ -1900,7 +1848,7 @@ func file_api_proto_init() { } } file_api_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PreCommitRes); i { + switch v := v.(*ReadAtRes); i { case 0: return &v.state case 1: @@ -1912,7 +1860,7 @@ func file_api_proto_init() { } } file_api_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommitReq); i { + switch v := v.(*ResetEpochReq); i { case 0: return &v.state case 1: @@ -1924,7 +1872,7 @@ func file_api_proto_init() { } } file_api_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CommitRes); i { + switch v := v.(*ResetEpochRes); i { case 0: return &v.state case 1: @@ -1936,30 +1884,6 @@ func file_api_proto_init() { } } file_api_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadAtReq); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_api_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ReadAtRes); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_api_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BlobInfoReq); i { case 0: return &v.state @@ -1971,7 +1895,7 @@ func file_api_proto_init() { return nil } } - file_api_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + file_api_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListBlobInfoReq); i { case 0: return &v.state @@ -1983,7 +1907,7 @@ func file_api_proto_init() { return nil } } - file_api_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_api_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BlobInfoRes); i { case 0: return &v.state @@ -1995,7 +1919,7 @@ func file_api_proto_init() { return nil } } - file_api_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_api_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SendUpdateReq); i { case 0: return &v.state @@ -2007,7 +1931,7 @@ func file_api_proto_init() { return nil } } - file_api_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_api_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SendUpdateRes); i { case 0: return &v.state @@ -2026,7 +1950,7 @@ func file_api_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_proto_rawDesc, NumEnums: 0, - NumMessages: 26, + NumMessages: 24, NumExtensions: 0, NumServices: 1, }, @@ -2058,10 +1982,9 @@ type Footnotev1Client interface { UnbanPeer(ctx context.Context, in *UnbanPeerReq, opts ...grpc.CallOption) (*Empty, error) ListPeers(ctx context.Context, in *ListPeersReq, opts ...grpc.CallOption) (Footnotev1_ListPeersClient, error) Checkout(ctx context.Context, in *CheckoutReq, opts ...grpc.CallOption) (*CheckoutRes, error) - WriteAt(ctx context.Context, in *WriteAtReq, opts ...grpc.CallOption) (*WriteAtRes, error) - Truncate(ctx context.Context, in *TruncateReq, opts ...grpc.CallOption) (*Empty, error) - PreCommit(ctx context.Context, in *PreCommitReq, opts ...grpc.CallOption) (*PreCommitRes, error) + WriteSector(ctx context.Context, in *WriteSectorReq, opts ...grpc.CallOption) (*WriteSectorRes, error) Commit(ctx context.Context, in *CommitReq, opts ...grpc.CallOption) (*CommitRes, error) + ResetEpoch(ctx context.Context, in *ResetEpochReq, opts ...grpc.CallOption) (*ResetEpochRes, error) ReadAt(ctx context.Context, in *ReadAtReq, opts ...grpc.CallOption) (*ReadAtRes, error) GetBlobInfo(ctx context.Context, in *BlobInfoReq, opts ...grpc.CallOption) (*BlobInfoRes, error) ListBlobInfo(ctx context.Context, in *ListBlobInfoReq, opts ...grpc.CallOption) (Footnotev1_ListBlobInfoClient, error) @@ -2153,36 +2076,27 @@ func (c *footnotev1Client) Checkout(ctx context.Context, in *CheckoutReq, opts . return out, nil } -func (c *footnotev1Client) WriteAt(ctx context.Context, in *WriteAtReq, opts ...grpc.CallOption) (*WriteAtRes, error) { - out := new(WriteAtRes) - err := c.cc.Invoke(ctx, "/Footnotev1/WriteAt", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *footnotev1Client) Truncate(ctx context.Context, in *TruncateReq, opts ...grpc.CallOption) (*Empty, error) { - out := new(Empty) - err := c.cc.Invoke(ctx, "/Footnotev1/Truncate", in, out, opts...) +func (c *footnotev1Client) WriteSector(ctx context.Context, in *WriteSectorReq, opts ...grpc.CallOption) (*WriteSectorRes, error) { + out := new(WriteSectorRes) + err := c.cc.Invoke(ctx, "/Footnotev1/WriteSector", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *footnotev1Client) PreCommit(ctx context.Context, in *PreCommitReq, opts ...grpc.CallOption) (*PreCommitRes, error) { - out := new(PreCommitRes) - err := c.cc.Invoke(ctx, "/Footnotev1/PreCommit", in, out, opts...) +func (c *footnotev1Client) Commit(ctx context.Context, in *CommitReq, opts ...grpc.CallOption) (*CommitRes, error) { + out := new(CommitRes) + err := c.cc.Invoke(ctx, "/Footnotev1/Commit", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *footnotev1Client) Commit(ctx context.Context, in *CommitReq, opts ...grpc.CallOption) (*CommitRes, error) { - out := new(CommitRes) - err := c.cc.Invoke(ctx, "/Footnotev1/Commit", in, out, opts...) +func (c *footnotev1Client) ResetEpoch(ctx context.Context, in *ResetEpochReq, opts ...grpc.CallOption) (*ResetEpochRes, error) { + out := new(ResetEpochRes) + err := c.cc.Invoke(ctx, "/Footnotev1/ResetEpoch", in, out, opts...) if err != nil { return nil, err } @@ -2256,10 +2170,9 @@ type Footnotev1Server interface { UnbanPeer(context.Context, *UnbanPeerReq) (*Empty, error) ListPeers(*ListPeersReq, Footnotev1_ListPeersServer) error Checkout(context.Context, *CheckoutReq) (*CheckoutRes, error) - WriteAt(context.Context, *WriteAtReq) (*WriteAtRes, error) - Truncate(context.Context, *TruncateReq) (*Empty, error) - PreCommit(context.Context, *PreCommitReq) (*PreCommitRes, error) + WriteSector(context.Context, *WriteSectorReq) (*WriteSectorRes, error) Commit(context.Context, *CommitReq) (*CommitRes, error) + ResetEpoch(context.Context, *ResetEpochReq) (*ResetEpochRes, error) ReadAt(context.Context, *ReadAtReq) (*ReadAtRes, error) GetBlobInfo(context.Context, *BlobInfoReq) (*BlobInfoRes, error) ListBlobInfo(*ListBlobInfoReq, Footnotev1_ListBlobInfoServer) error @@ -2288,18 +2201,15 @@ func (*UnimplementedFootnotev1Server) ListPeers(*ListPeersReq, Footnotev1_ListPe func (*UnimplementedFootnotev1Server) Checkout(context.Context, *CheckoutReq) (*CheckoutRes, error) { return nil, status.Errorf(codes.Unimplemented, "method Checkout not implemented") } -func (*UnimplementedFootnotev1Server) WriteAt(context.Context, *WriteAtReq) (*WriteAtRes, error) { - return nil, status.Errorf(codes.Unimplemented, "method WriteAt not implemented") -} -func (*UnimplementedFootnotev1Server) Truncate(context.Context, *TruncateReq) (*Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Truncate not implemented") -} -func (*UnimplementedFootnotev1Server) PreCommit(context.Context, *PreCommitReq) (*PreCommitRes, error) { - return nil, status.Errorf(codes.Unimplemented, "method PreCommit not implemented") +func (*UnimplementedFootnotev1Server) WriteSector(context.Context, *WriteSectorReq) (*WriteSectorRes, error) { + return nil, status.Errorf(codes.Unimplemented, "method WriteSector not implemented") } func (*UnimplementedFootnotev1Server) Commit(context.Context, *CommitReq) (*CommitRes, error) { return nil, status.Errorf(codes.Unimplemented, "method Commit not implemented") } +func (*UnimplementedFootnotev1Server) ResetEpoch(context.Context, *ResetEpochReq) (*ResetEpochRes, error) { + return nil, status.Errorf(codes.Unimplemented, "method ResetEpoch not implemented") +} func (*UnimplementedFootnotev1Server) ReadAt(context.Context, *ReadAtReq) (*ReadAtRes, error) { return nil, status.Errorf(codes.Unimplemented, "method ReadAt not implemented") } @@ -2428,74 +2338,56 @@ func _Footnotev1_Checkout_Handler(srv interface{}, ctx context.Context, dec func return interceptor(ctx, in, info, handler) } -func _Footnotev1_WriteAt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(WriteAtReq) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(Footnotev1Server).WriteAt(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/Footnotev1/WriteAt", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(Footnotev1Server).WriteAt(ctx, req.(*WriteAtReq)) - } - return interceptor(ctx, in, info, handler) -} - -func _Footnotev1_Truncate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(TruncateReq) +func _Footnotev1_WriteSector_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(WriteSectorReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(Footnotev1Server).Truncate(ctx, in) + return srv.(Footnotev1Server).WriteSector(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/Footnotev1/Truncate", + FullMethod: "/Footnotev1/WriteSector", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(Footnotev1Server).Truncate(ctx, req.(*TruncateReq)) + return srv.(Footnotev1Server).WriteSector(ctx, req.(*WriteSectorReq)) } return interceptor(ctx, in, info, handler) } -func _Footnotev1_PreCommit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PreCommitReq) +func _Footnotev1_Commit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CommitReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(Footnotev1Server).PreCommit(ctx, in) + return srv.(Footnotev1Server).Commit(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/Footnotev1/PreCommit", + FullMethod: "/Footnotev1/Commit", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(Footnotev1Server).PreCommit(ctx, req.(*PreCommitReq)) + return srv.(Footnotev1Server).Commit(ctx, req.(*CommitReq)) } return interceptor(ctx, in, info, handler) } -func _Footnotev1_Commit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CommitReq) +func _Footnotev1_ResetEpoch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResetEpochReq) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(Footnotev1Server).Commit(ctx, in) + return srv.(Footnotev1Server).ResetEpoch(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/Footnotev1/Commit", + FullMethod: "/Footnotev1/ResetEpoch", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(Footnotev1Server).Commit(ctx, req.(*CommitReq)) + return srv.(Footnotev1Server).ResetEpoch(ctx, req.(*ResetEpochReq)) } return interceptor(ctx, in, info, handler) } @@ -2600,21 +2492,17 @@ var _Footnotev1_serviceDesc = grpc.ServiceDesc{ Handler: _Footnotev1_Checkout_Handler, }, { - MethodName: "WriteAt", - Handler: _Footnotev1_WriteAt_Handler, - }, - { - MethodName: "Truncate", - Handler: _Footnotev1_Truncate_Handler, - }, - { - MethodName: "PreCommit", - Handler: _Footnotev1_PreCommit_Handler, + MethodName: "WriteSector", + Handler: _Footnotev1_WriteSector_Handler, }, { MethodName: "Commit", Handler: _Footnotev1_Commit_Handler, }, + { + MethodName: "ResetEpoch", + Handler: _Footnotev1_ResetEpoch_Handler, + }, { MethodName: "ReadAt", Handler: _Footnotev1_ReadAt_Handler, diff --git a/rpc/v1/api.proto b/rpc/v1/api.proto index 14eb0d7..e400c54 100644 --- a/rpc/v1/api.proto +++ b/rpc/v1/api.proto @@ -10,13 +10,13 @@ service Footnotev1 { rpc ListPeers (ListPeersReq) returns (stream ListPeersRes); rpc Checkout (CheckoutReq) returns (CheckoutRes); - rpc WriteAt (WriteAtReq) returns (WriteAtRes); - rpc Truncate (TruncateReq) returns (Empty); - rpc PreCommit (PreCommitReq) returns (PreCommitRes); + rpc WriteSector (WriteSectorReq) returns (WriteSectorRes); rpc Commit (CommitReq) returns (CommitRes); + rpc ResetEpoch(ResetEpochReq) returns (ResetEpochRes); rpc ReadAt (ReadAtReq) returns (ReadAtRes); + rpc GetBlobInfo (BlobInfoReq) returns (BlobInfoRes); rpc ListBlobInfo (ListBlobInfoReq) returns (stream BlobInfoRes); @@ -78,32 +78,18 @@ message CheckoutReq { message CheckoutRes { uint32 txID = 1; + uint32 epochHeight = 2; + uint32 sectorSize = 3; + bytes sectorTipHash = 4; } -message WriteAtReq { - uint32 txID = 1; - uint32 offset = 2; - bytes data = 3; -} - -message WriteAtRes { - uint32 bytesWritten = 1; - string writeErr = 2; -} - -message TruncateReq { - uint32 txID = 1; -} - -message TruncateRes { -} - -message PreCommitReq { +message WriteSectorReq { uint32 txID = 1; + bytes data = 2; } -message PreCommitRes { - bytes merkleRoot = 1; +message WriteSectorRes { + string writeErr = 1; } message CommitReq { @@ -111,6 +97,9 @@ message CommitReq { uint64 timestamp = 2; bytes signature = 3; bool broadcast = 4; + uint32 epochHeight = 5; + uint32 sectorSize = 6; + bytes sectorTipHash = 7; } message CommitRes { @@ -127,6 +116,13 @@ message ReadAtRes { bytes data = 2; } +message ResetEpochReq { + uint32 txID = 1; +} + +message ResetEpochRes { +} + message BlobInfoReq { string name = 1; } @@ -139,12 +135,13 @@ message BlobInfoRes { string name = 1; bytes publicKey = 2; uint32 importHeight = 3; - uint64 timestamp = 4; - bytes merkleRoot = 5; - bytes reservedRoot = 6; - uint64 receivedAt = 7; + uint32 epochHeight = 4; // protobuf doesn't have uint16 + uint32 sectorSize = 5; // ditto ^ + bytes sectorTipHash = 6; + bytes reservedRoot = 7; bytes signature = 8; - uint32 timebank = 9; + uint64 receivedAt = 9; + uint64 bannedAt = 10; } message SendUpdateReq { diff --git a/store/equivocation_proof.go b/store/equivocation_proof.go new file mode 100644 index 0000000..ac43d73 --- /dev/null +++ b/store/equivocation_proof.go @@ -0,0 +1,31 @@ +package store + +import ( + "bytes" + "fnd/wire" + + "github.com/pkg/errors" + "github.com/syndtr/goleveldb/leveldb" +) + +var ( + equivocationProofsPrefix = Prefixer("equivocationproofs") +) + +func GetEquivocationProof(db *leveldb.DB, name string) ([]byte, error) { + bytes, err := db.Get(equivocationProofsPrefix(name), nil) + if err != nil { + return nil, errors.Wrap(err, "error getting equivocation proof") + } + return bytes, nil +} + +func SetEquivocationProofTx(tx *leveldb.Transaction, name string, proof *wire.EquivocationProof) error { + var buf bytes.Buffer + proof.Encode(&buf) + err := tx.Put(equivocationProofsPrefix(name), buf.Bytes(), nil) + if err != nil { + return errors.Wrap(err, "error setting equivocation proof") + } + return nil +} diff --git a/store/headers.go b/store/headers.go index 6c9efdc..adafa81 100644 --- a/store/headers.go +++ b/store/headers.go @@ -4,44 +4,45 @@ import ( "bytes" "encoding/hex" "encoding/json" - "github.com/btcsuite/btcd/btcec" "fnd/blob" "fnd/crypto" + "sync" + "time" + + "github.com/btcsuite/btcd/btcec" "github.com/pkg/errors" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/util" - "sync" - "time" ) type Header struct { - Name string - Timestamp time.Time - MerkleRoot crypto.Hash - Signature crypto.Signature - ReservedRoot crypto.Hash - ReceivedAt time.Time - Timebank int + Name string + EpochHeight uint16 + SectorSize uint16 + SectorTipHash crypto.Hash + Signature crypto.Signature + ReservedRoot crypto.Hash + EpochStartAt time.Time } func (h *Header) MarshalJSON() ([]byte, error) { out := &struct { - Name string `json:"name"` - Timestamp time.Time `json:"timestamp"` - MerkleRoot string `json:"merkle_root"` - Signature string `json:"signature"` - ReservedRoot string `json:"reserved_root"` - ReceivedAt time.Time `json:"received_at"` - Timebank int `json:"timebank"` + Name string `json:"name"` + EpochHeight uint16 `json:"epoch_height"` + SectorSize uint16 `json:"sector_size"` + SectorTipHash string `json:"sector_tip_hash"` + Signature string `json:"signature"` + ReservedRoot string `json:"reserved_root"` + EpochStartAt time.Time `json:"epoch_start_at"` }{ h.Name, - h.Timestamp, - h.MerkleRoot.String(), + h.EpochHeight, + h.SectorSize, + h.SectorTipHash.String(), h.Signature.String(), h.ReservedRoot.String(), - h.ReceivedAt, - h.Timebank, + h.EpochStartAt, } return json.Marshal(out) @@ -49,18 +50,18 @@ func (h *Header) MarshalJSON() ([]byte, error) { func (h *Header) UnmarshalJSON(b []byte) error { in := &struct { - Name string `json:"name"` - Timestamp time.Time `json:"timestamp"` - MerkleRoot string `json:"merkle_root"` - Signature string `json:"signature"` - ReservedRoot string `json:"reserved_root"` - ReceivedAt time.Time `json:"received_at"` - Timebank int `json:"timebank"` + Name string `json:"name"` + EpochHeight uint16 `json:"epoch_height"` + SectorSize uint16 `json:"sector_size"` + SectorTipHash string `json:"sector_tip_hash"` + Signature string `json:"signature"` + ReservedRoot string `json:"reserved_root"` + EpochStartAt time.Time `json:"epoch_start_at"` }{} if err := json.Unmarshal(b, in); err != nil { return err } - mrB, err := hex.DecodeString(in.MerkleRoot) + mrB, err := hex.DecodeString(in.SectorTipHash) if err != nil { return err } @@ -86,20 +87,21 @@ func (h *Header) UnmarshalJSON(b []byte) error { } h.Name = in.Name - h.Timestamp = in.Timestamp - h.MerkleRoot = mr + h.EpochHeight = in.EpochHeight + h.SectorSize = in.SectorSize + h.SectorTipHash = mr h.Signature = sig h.ReservedRoot = rr - h.ReceivedAt = in.ReceivedAt - h.Timebank = in.Timebank + h.EpochStartAt = in.EpochStartAt return nil } var ( - headersPrefix = Prefixer("headers") - headerCountKey = Prefixer(string(headersPrefix("count")))() - headerMerkleBasePrefix = Prefixer(string(headersPrefix("merkle-base"))) - headerDataPrefix = Prefixer(string(headersPrefix("header"))) + headersPrefix = Prefixer("headers") + headerCountKey = Prefixer(string(headersPrefix("count")))() + headerSectorHashesPrefix = Prefixer(string(headersPrefix("sector-hashes"))) + headerBanPrefix = Prefixer(string(headersPrefix("banned"))) + headerDataPrefix = Prefixer(string(headersPrefix("header"))) ) func GetHeaderCount(db *leveldb.DB) (int, error) { @@ -138,11 +140,22 @@ func GetHeader(db *leveldb.DB, name string) (*Header, error) { return header, nil } -func GetMerkleBase(db *leveldb.DB, name string) (blob.MerkleBase, error) { - var base blob.MerkleBase - baseB, err := db.Get(headerMerkleBasePrefix(name), nil) +func GetSectorHash(db *leveldb.DB, name string, index uint16) (crypto.Hash, error) { + hashes, err := GetSectorHashes(db, name) if err != nil { - return base, errors.Wrap(err, "error getting merkle base") + return crypto.ZeroHash, err + } + if int(index) > len(hashes) { + return crypto.ZeroHash, errors.Wrap(err, "error getting index") + } + return hashes[index], nil +} + +func GetSectorHashes(db *leveldb.DB, name string) (blob.SectorHashes, error) { + var base blob.SectorHashes + baseB, err := db.Get(headerSectorHashesPrefix(name), nil) + if err != nil { + return base, errors.Wrap(err, "error getting sector hashes") } if err := base.Decode(bytes.NewReader(baseB)); err != nil { panic(err) @@ -150,17 +163,43 @@ func GetMerkleBase(db *leveldb.DB, name string) (blob.MerkleBase, error) { return base, nil } -func SetHeaderTx(tx *leveldb.Transaction, header *Header, merkleBase blob.MerkleBase) error { +func GetHeaderBan(db *leveldb.DB, name string) (time.Time, error) { + exists, err := db.Has(headerBanPrefix(name), nil) + if err != nil { + return time.Time{}, errors.Wrap(err, "error checking header ban") + } + if !exists { + return time.Time{}, nil + } + bytes, err := db.Get(headerBanPrefix(name), nil) + if err != nil { + return time.Time{}, errors.Wrap(err, "error getting header ban") + } + timestamp := mustDecodeInt(bytes) + return time.Unix(int64(timestamp), 0), nil +} + +func SetHeaderBan(tx *leveldb.Transaction, name string, at time.Time) error { + if at.IsZero() { + at = time.Now() + } + if err := tx.Put(headerBanPrefix(name), mustEncodeInt(int(at.Unix())), nil); err != nil { + return errors.Wrap(err, "error writing header tree") + } + return nil +} + +func SetHeaderTx(tx *leveldb.Transaction, header *Header, sectorHashes blob.SectorHashes) error { var buf bytes.Buffer - if err := merkleBase.Encode(&buf); err != nil { - return errors.Wrap(err, "error encoding merkle tree") + if err := sectorHashes.Encode(&buf); err != nil { + return errors.Wrap(err, "error encoding sector hashes") } exists, err := tx.Has(headerDataPrefix(header.Name), nil) if err != nil { return errors.Wrap(err, "error checking header existence") } - if err := tx.Put(headerMerkleBasePrefix(header.Name), buf.Bytes(), nil); err != nil { - return errors.Wrap(err, "error writing merkle tree") + if err := tx.Put(headerSectorHashesPrefix(header.Name), buf.Bytes(), nil); err != nil { + return errors.Wrap(err, "error writing sector hashes") } if err := tx.Put(headerDataPrefix(header.Name), mustMarshalJSON(header), nil); err != nil { return errors.Wrap(err, "error writing header tree") @@ -174,38 +213,41 @@ func SetHeaderTx(tx *leveldb.Transaction, header *Header, merkleBase blob.Merkle } type BlobInfo struct { - Name string `json:"name"` - PublicKey *btcec.PublicKey `json:"public_key"` - ImportHeight int `json:"import_height"` - Timestamp time.Time `json:"timestamp"` - MerkleRoot crypto.Hash `json:"merkle_root"` - Signature crypto.Signature `json:"signature"` - ReservedRoot crypto.Hash `json:"reserved_root"` - ReceivedAt time.Time `json:"received_at"` - Timebank int `json:"timebank"` + Name string `json:"name"` + PublicKey *btcec.PublicKey `json:"public_key"` + ImportHeight int `json:"import_height"` + EpochHeight uint16 `json:"epoch_height"` + SectorSize uint16 `json:"sector_size"` + SectorTipHash crypto.Hash `json:"sector_tip_hash"` + Signature crypto.Signature `json:"signature"` + ReservedRoot crypto.Hash `json:"reserved_root"` + ReceivedAt time.Time `json:"received_at"` + BannedAt time.Time `json:"banned_at"` } func (b *BlobInfo) MarshalJSON() ([]byte, error) { jsonInfo := struct { - Name string `json:"name"` - PublicKey string `json:"public_key"` - ImportHeight int `json:"import_height"` - Timestamp time.Time `json:"timestamp"` - MerkleRoot string `json:"merkle_root"` - Signature string `json:"signature"` - ReservedRoot string `json:"reserved_root"` - ReceivedAt time.Time `json:"received_at"` - Timebank int `json:"timebank"` + Name string `json:"name"` + PublicKey string `json:"public_key"` + ImportHeight int `json:"import_height"` + EpochHeight uint16 `json:"epoch_height"` + SectorSize uint16 `json:"sector_size"` + SectorTipHash string `json:"sector_tip_hash"` + Signature string `json:"signature"` + ReservedRoot string `json:"reserved_root"` + ReceivedAt time.Time `json:"received_at"` + BannedAt time.Time `json:"banned_at"` }{ b.Name, hex.EncodeToString(b.PublicKey.SerializeCompressed()), b.ImportHeight, - b.Timestamp, - hex.EncodeToString(b.MerkleRoot[:]), + b.EpochHeight, + b.SectorSize, + hex.EncodeToString(b.SectorTipHash[:]), hex.EncodeToString(b.Signature[:]), hex.EncodeToString(b.ReservedRoot[:]), b.ReceivedAt, - b.Timebank, + b.BannedAt, } return json.Marshal(jsonInfo) @@ -227,16 +269,21 @@ func (bis *BlobInfoStream) Next() (*BlobInfo, error) { if err != nil { return nil, errors.Wrap(err, "error getting name info") } + bannedAt, err := GetHeaderBan(bis.db, header.Name) + if err != nil { + return nil, errors.Wrap(err, "error getting header ban info") + } return &BlobInfo{ - Name: header.Name, - PublicKey: nameInfo.PublicKey, - ImportHeight: nameInfo.ImportHeight, - Timestamp: header.Timestamp, - MerkleRoot: header.MerkleRoot, - Signature: header.Signature, - ReservedRoot: header.ReservedRoot, - ReceivedAt: header.ReceivedAt, - Timebank: header.Timebank, + Name: header.Name, + PublicKey: nameInfo.PublicKey, + ImportHeight: nameInfo.ImportHeight, + EpochHeight: header.EpochHeight, + SectorSize: header.SectorSize, + SectorTipHash: header.SectorTipHash, + Signature: header.Signature, + ReservedRoot: header.ReservedRoot, + ReceivedAt: header.EpochStartAt, + BannedAt: bannedAt, }, nil } @@ -266,6 +313,19 @@ func StreamBlobInfo(db *leveldb.DB, start string) (*BlobInfoStream, error) { }, nil } +func TruncateHeaderName(db *leveldb.DB, name string) error { + err := WithTx(db, func(tx *leveldb.Transaction) error { + if err := tx.Delete(headerDataPrefix(name), nil); err != nil { + return errors.Wrap(err, "error deleting header store key") + } + return nil + }) + if err != nil { + return errors.Wrap(err, "error truncating header store") + } + return nil +} + func TruncateHeaderStore(db *leveldb.DB) error { err := WithTx(db, func(tx *leveldb.Transaction) error { iter := tx.NewIterator(util.BytesPrefix(headersPrefix()), nil) diff --git a/store/headers_test.go b/store/headers_test.go index f6276b7..7eb7271 100644 --- a/store/headers_test.go +++ b/store/headers_test.go @@ -13,7 +13,7 @@ import ( func TestHeaders_GetSet(t *testing.T) { db, done := setupLevelDB(t) - var expMB blob.MerkleBase + var expMB blob.SectorHashes _, err := rand.Read(expMB[0][:]) require.NoError(t, err) @@ -22,13 +22,13 @@ func TestHeaders_GetSet(t *testing.T) { require.NoError(t, err) expHeader := &Header{ - Name: "foo", - Timestamp: time.Unix(10, 0), - MerkleRoot: crypto.Rand32(), - Signature: sig, - ReservedRoot: crypto.Rand32(), - ReceivedAt: time.Unix(11, 0), - Timebank: 100, + Name: "foo", + EpochHeight: uint16(0), + SectorSize: uint16(0), + SectorTipHash: crypto.Rand32(), + Signature: sig, + ReservedRoot: crypto.Rand32(), + EpochStartAt: time.Unix(11, 0), } _, err = GetHeader(db, "foo") require.Error(t, err) @@ -38,12 +38,13 @@ func TestHeaders_GetSet(t *testing.T) { actHeader, err := GetHeader(db, "foo") require.NoError(t, err) require.Equal(t, expHeader.Name, actHeader.Name) - require.Equal(t, expHeader.Timestamp.Unix(), actHeader.Timestamp.Unix()) - require.Equal(t, expHeader.MerkleRoot, actHeader.MerkleRoot) + require.Equal(t, expHeader.EpochHeight, actHeader.EpochHeight) + require.Equal(t, expHeader.SectorSize, actHeader.SectorSize) + require.Equal(t, expHeader.SectorTipHash, actHeader.SectorTipHash) require.Equal(t, expHeader.Signature, actHeader.Signature) require.Equal(t, expHeader.ReservedRoot, actHeader.ReservedRoot) - require.Equal(t, expHeader.ReceivedAt.Unix(), actHeader.ReceivedAt.Unix()) - actMB, err := GetMerkleBase(db, "foo") + require.Equal(t, expHeader.EpochStartAt.Unix(), actHeader.EpochStartAt.Unix()) + actMB, err := GetSectorHashes(db, "foo") require.NoError(t, err) require.Equal(t, expMB, actMB) diff --git a/testutil/mockapp/null.go b/testutil/mockapp/null.go new file mode 100644 index 0000000..8e60b8a --- /dev/null +++ b/testutil/mockapp/null.go @@ -0,0 +1,32 @@ +package mockapp + +import ( + "io" + "io/ioutil" +) + +type reader struct{} + +func (reader) Read(b []byte) (int, error) { + for i := range b { + b[i] = 0 + } + return len(b), nil +} + +var NullReader io.Reader + +func Read(b []byte) (n int, err error) { + return NullReader.Read(b) +} + +var NullWriter io.Writer + +func Write(p []byte) (n int, err error) { + return NullWriter.Write(p) +} + +func init() { + NullReader = new(reader) + NullWriter = ioutil.Discard +} diff --git a/testutil/mockapp/storage.go b/testutil/mockapp/storage.go index b527cea..4367908 100644 --- a/testutil/mockapp/storage.go +++ b/testutil/mockapp/storage.go @@ -7,11 +7,12 @@ import ( "fnd/store" "fnd/testutil/testfs" "fnd/wire" - "github.com/stretchr/testify/require" - "github.com/syndtr/goleveldb/leveldb" "io" "testing" "time" + + "github.com/stretchr/testify/require" + "github.com/syndtr/goleveldb/leveldb" ) type TestStorage struct { @@ -45,45 +46,45 @@ func CreateTestDB(t *testing.T) (*leveldb.DB, func()) { return db, done } -func FillBlobReader(t *testing.T, db *leveldb.DB, bs blob.Store, signer crypto.Signer, name string, ts time.Time, receivedAt time.Time, r io.Reader) *wire.Update { +func FillBlobReader(t *testing.T, db *leveldb.DB, bs blob.Store, signer crypto.Signer, name string, epochHeight, sectorSize uint16, receivedAt time.Time, r io.Reader) *wire.Update { bl, err := bs.Open(name) require.NoError(t, err) tx, err := bl.Transaction() require.NoError(t, err) _, err = io.Copy(blob.NewWriter(tx), io.LimitReader(r, blob.Size)) require.NoError(t, err) - tree, err := blob.Merkleize(blob.NewReader(tx)) + tree, err := blob.SerialHash(blob.NewReader(tx), blob.ZeroHash, sectorSize) require.NoError(t, err) - sig, err := blob.SignSeal(signer, name, ts, tree.Root(), crypto.ZeroHash) + sig, err := blob.SignSeal(signer, name, epochHeight, sectorSize, tree.Tip(), crypto.ZeroHash) require.NoError(t, err) require.NoError(t, store.WithTx(db, func(tx *leveldb.Transaction) error { return store.SetHeaderTx(tx, &store.Header{ - Name: name, - Timestamp: ts, - MerkleRoot: tree.Root(), - Signature: sig, - ReservedRoot: crypto.ZeroHash, - ReceivedAt: receivedAt, - }, tree.ProtocolBase()) + Name: name, + EpochHeight: epochHeight, + SectorSize: sectorSize, + SectorTipHash: tree.Tip(), + Signature: sig, + ReservedRoot: crypto.ZeroHash, + EpochStartAt: receivedAt, + }, tree) })) require.NoError(t, tx.Commit()) return &wire.Update{ - Name: name, - Timestamp: ts, - MerkleRoot: tree.Root(), - ReservedRoot: crypto.ZeroHash, - Signature: sig, + Name: name, + EpochHeight: epochHeight, + SectorSize: sectorSize, } } -func FillBlobRandom(t *testing.T, db *leveldb.DB, bs blob.Store, signer crypto.Signer, name string, ts time.Time, receivedAt time.Time) *wire.Update { +func FillBlobRandom(t *testing.T, db *leveldb.DB, bs blob.Store, signer crypto.Signer, name string, epochHeight, sectorSize uint16, receivedAt time.Time) *wire.Update { return FillBlobReader( t, db, bs, signer, name, - ts, + epochHeight, + sectorSize, receivedAt, rand.Reader, ) @@ -94,7 +95,7 @@ func RequireBlobsEqual(t *testing.T, localBS blob.Store, remoteBS blob.Store, na require.NoError(t, err) remoteBl, err := remoteBS.Open(name) require.NoError(t, err) - for i := 0; i < blob.SectorCount; i++ { + for i := 0; i < blob.MaxSectors; i++ { localSector, err := localBl.ReadSector(uint8(i)) require.NoError(t, err) remoteSector, err := remoteBl.ReadSector(uint8(i)) diff --git a/vendor/fnd.localhost/dwire/encoder.go b/vendor/fnd.localhost/dwire/encoder.go index 3b358f8..a259f8a 100644 --- a/vendor/fnd.localhost/dwire/encoder.go +++ b/vendor/fnd.localhost/dwire/encoder.go @@ -3,8 +3,8 @@ package dwire import "io" const ( - DefaultMaxVariableArrayLen = 1024 - DefaultMaxByteFieldLen = 256 * 1024 + DefaultMaxVariableArrayLen = 4 * 1024 + DefaultMaxByteFieldLen = 8 * 256 * 1024 ) // Encoder is an interface that allows arbitrary types to be @@ -33,7 +33,7 @@ type ConfiguredEncoder struct { // MaxByteFieldLen is the maximum length of a variable-length byte array field // dwire will decode before stopping early. - MaxByteFieldLen uint64 + MaxByteFieldLen uint64 } var defaultEncoder = &ConfiguredEncoder{ diff --git a/vendor/fnd.localhost/handshake/client/rest_types.go b/vendor/fnd.localhost/handshake/client/rest_types.go index 95dc63e..1ac2e53 100644 --- a/vendor/fnd.localhost/handshake/client/rest_types.go +++ b/vendor/fnd.localhost/handshake/client/rest_types.go @@ -21,7 +21,7 @@ type ChainInfo struct { Height int `json:"height"` Tip string `json:"tip"` TreeRoot string `json:"treeRoot"` - Progress int `json:"progress"` + Progress float64 `json:"progress"` State StateInfo `json:"state"` } diff --git a/vendor/fnd.localhost/mstream/.gitignore b/vendor/fnd.localhost/mstream/.gitignore deleted file mode 100644 index ea34f98..0000000 --- a/vendor/fnd.localhost/mstream/.gitignore +++ /dev/null @@ -1,18 +0,0 @@ -# Created by .ignore support plugin (hsz.mobi) -### Go template -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Dependency directories (remove the comment below to include it) -# vendor/ - diff --git a/vendor/fnd.localhost/mstream/Makefile b/vendor/fnd.localhost/mstream/Makefile deleted file mode 100644 index 98eb341..0000000 --- a/vendor/fnd.localhost/mstream/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -test: - go test ./... --race - -fmt: - goimports -w . - gofmt -s -w . - -.PHONY: test fmt diff --git a/vendor/fnd.localhost/mstream/decode.go b/vendor/fnd.localhost/mstream/decode.go deleted file mode 100644 index 9349e43..0000000 --- a/vendor/fnd.localhost/mstream/decode.go +++ /dev/null @@ -1,219 +0,0 @@ -package mstream - -import ( - "encoding/binary" - "fmt" - "io" - "reflect" - - "github.com/pkg/errors" -) - -type byteReader struct { - r io.Reader - buf []byte -} - -func newByteReader(r io.Reader) *byteReader { - return &byteReader{ - r: r, - buf: make([]byte, 1, 1), - } -} - -func (r *byteReader) Read(p []byte) (int, error) { - return r.r.Read(p) -} - -func (r *byteReader) ReadByte() (byte, error) { - _, err := io.ReadFull(r.r, r.buf) - if err != nil { - return 0, err - } - return r.buf[0], nil -} - -func DecodeFields(r io.Reader, items ...interface{}) error { - return defaultEncoder.DecodeFields(r, items...) -} - -func DecodeField(r io.Reader, item interface{}) error { - return defaultEncoder.DecodeField(r, item) -} - -func (c *ConfiguredEncoder) DecodeFields(r io.Reader, items ...interface{}) error { - for _, item := range items { - if err := c.DecodeField(r, item); err != nil { - return err - } - } - - return nil -} - -func (c *ConfiguredEncoder) DecodeField(r io.Reader, item interface{}) error { - var err error - switch it := item.(type) { - case Decoder: - err = it.Decode(r) - case *bool: - b := make([]byte, 1, 1) - if _, err := io.ReadFull(r, b); err != nil { - return err - } - if b[0] == 0x00 { - *it = false - } else if b[0] == 0x01 { - *it = true - } else { - return errors.Errorf("invalid boolean value: %x", b[0]) - } - case *uint8: - b := make([]byte, 1, 1) - if _, err := io.ReadFull(r, b); err != nil { - return err - } - *it = b[0] - case *uint16: - b := make([]byte, 2, 2) - if _, err := io.ReadFull(r, b); err != nil { - return err - } - *it = binary.BigEndian.Uint16(b) - case *uint32: - b := make([]byte, 4, 4) - if _, err := io.ReadFull(r, b); err != nil { - return err - } - *it = binary.BigEndian.Uint32(b) - case *uint64: - b := make([]byte, 8, 8) - if _, err := io.ReadFull(r, b); err != nil { - return err - } - *it = binary.BigEndian.Uint64(b) - case *[]byte: - br := newByteReader(r) - l, err := binary.ReadUvarint(br) - if err != nil { - return err - } - if l > c.MaxByteFieldLen { - return errors.New("byte-assignable field length too large to decode") - } - buf := make([]byte, l, l) - if _, err := io.ReadFull(r, buf); err != nil { - return err - } - *it = buf - case *string: - var buf []byte - if err := c.DecodeField(r, &buf); err != nil { - return err - } - *it = string(buf) - case *[32]byte: - var buf [32]byte - if _, err := io.ReadFull(r, buf[:]); err != nil { - return err - } - *it = buf - default: - err = c.decodeReflect(r, item) - } - - return err -} - -func (c *ConfiguredEncoder) decodeReflect(r io.Reader, item interface{}) error { - itemT := reflect.TypeOf(item) - if itemT.Kind() != reflect.Ptr { - return errors.New("can only decode into pointer types") - } - - canonicalized := canonicalizeWellKnown(itemT.Elem()) - if wellKnownDecoders[canonicalized] != nil { - return wellKnownDecoders[canonicalized](r, item) - } - - elemKind := itemT.Elem().Kind() - if itemT.Elem().Kind() == reflect.Array { - return c.decodeArray(r, item) - } - - if elemKind == reflect.Slice { - return c.decodeSlice(r, item) - } - - return errors.New(fmt.Sprintf("type %s cannot be decoded", itemT.String())) -} - -func (c *ConfiguredEncoder) decodeArray(r io.Reader, item interface{}) error { - itemVal := reflect.ValueOf(item) - indirectVal := reflect.Indirect(itemVal) - indirectT := indirectVal.Type() - - l := indirectT.Len() - tmp := reflect.Zero(reflect.ArrayOf(l, indirectT.Elem())) - tmpPtr := reflect.New(indirectT) - tmpPtr.Elem().Set(tmp) - if indirectT.Elem().Kind() == reflect.Uint8 { - buf := make([]byte, l, l) - if _, err := io.ReadFull(r, buf); err != nil { - return err - } - reflect.Copy(tmpPtr.Elem().Slice(0, l), reflect.ValueOf(buf)) - } else { - for i := 0; i < indirectVal.Len(); i++ { - if err := c.DecodeField(r, tmpPtr.Elem().Index(i).Addr().Interface()); err != nil { - return err - } - } - } - itemVal.Elem().Set(tmpPtr.Elem()) - return nil -} - -func (c *ConfiguredEncoder) decodeSlice(r io.Reader, item interface{}) error { - itemVal := reflect.ValueOf(item) - indirectVal := reflect.Indirect(itemVal) - indirectT := indirectVal.Type() - - tmp := reflect.Zero(reflect.SliceOf(indirectT.Elem())) - tmpPtr := reflect.New(indirectT) - tmpPtr.Elem().Set(tmp) - - br := newByteReader(r) - l, err := binary.ReadUvarint(br) - if err != nil { - return err - } - if l > uint64(c.MaxVariableArrayLen) { - return errors.New("variable array field length too large to decode") - } - - if indirectT.Elem().Kind() == reflect.Ptr { - for i := 0; i < int(l); i++ { - sliceItem := reflect.Zero(indirectT.Elem().Elem()) - sliceItemPtr := reflect.New(sliceItem.Type()) - sliceItemPtr.Elem().Set(sliceItem) - if err := c.DecodeField(r, sliceItemPtr.Interface()); err != nil { - return err - } - tmpPtr.Elem().Set(reflect.Append(tmpPtr.Elem(), sliceItemPtr)) - } - } else { - for i := 0; i < int(l); i++ { - sliceItem := reflect.Zero(indirectT.Elem()) - sliceItemPtr := reflect.New(indirectT.Elem()) - sliceItemPtr.Elem().Set(sliceItem) - if err := c.DecodeField(r, sliceItemPtr.Interface()); err != nil { - return err - } - tmpPtr.Elem().Set(reflect.Append(tmpPtr.Elem(), sliceItemPtr.Elem())) - } - } - - itemVal.Elem().Set(tmpPtr.Elem()) - return nil -} diff --git a/vendor/fnd.localhost/mstream/decode_test.go b/vendor/fnd.localhost/mstream/decode_test.go deleted file mode 100644 index 74e3d81..0000000 --- a/vendor/fnd.localhost/mstream/decode_test.go +++ /dev/null @@ -1,166 +0,0 @@ -package mstream - -import ( - "bytes" - "encoding/hex" - "errors" - "io" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -type cafeEncodeDecoder struct { - data []byte -} - -func (c *cafeEncodeDecoder) Decode(r io.Reader) error { - buf := make([]byte, 2, 2) - if _, err := io.ReadFull(r, buf); err != nil { - return err - } - if !bytes.Equal(buf, []byte{0xca, 0xfe}) { - return errors.New("invalid cafe decode") - } - c.data = buf - return nil -} - -func (c *cafeEncodeDecoder) Encode(w io.Writer) error { - _, err := w.Write([]byte{0xca, 0xfe}) - return err -} - -func TestDecodeFields(t *testing.T) { - var cafe cafeEncodeDecoder - - type testStruct struct { - f0 cafeEncodeDecoder - f1 uint8 - f2 uint16 - f3 uint32 - f4 uint64 - f5 []byte - f6 string - f7 [32]byte - f8 [2]uint8 - f9 []uint8 - f10 []string - f11 time.Time - f12 [2]string - f13 []*cafeEncodeDecoder - } - exp := &testStruct{ - f0: cafe, - f1: 1, - f2: 2, - f3: 3, - f4: 4, - f5: []byte{0xff, 0x00}, - f6: "testing", - f7: [32]byte{}, - f8: [2]uint8{ - 1, - 2, - }, - f9: []uint8{ - 3, - 4, - }, - f10: []string{ - "testing", - "testing", - }, - f11: time.Unix(1, 0), - f12: [2]string{ - "testing", - "testing", - }, - f13: []*cafeEncodeDecoder{ - &cafe, - &cafe, - }, - } - exp.f7[0] = 0x11 - - var actual testStruct - inputBytes, err := hex.DecodeString( - "cafe" + - "01" + - "0002" + - "00000003" + - "0000000000000004" + - "02ff00" + - "0774657374696e67" + - "1100000000000000000000000000000000000000000000000000000000000000" + - "0102" + - "020304" + - "020774657374696e670774657374696e67" + - "0000000000000001" + - "0774657374696e670774657374696e67" + - "02cafecafe", - ) - require.NoError(t, err) - require.NoError(t, DecodeFields( - bytes.NewReader(inputBytes), - &actual.f0, - &actual.f1, - &actual.f2, - &actual.f3, - &actual.f4, - &actual.f5, - &actual.f6, - &actual.f7, - &actual.f8, - &actual.f9, - &actual.f10, - &actual.f11, - &actual.f12, - &actual.f13, - )) - require.EqualValues(t, exp.f0.data, exp.f0.data) - require.EqualValues(t, exp.f1, actual.f1) - require.EqualValues(t, exp.f2, actual.f2) - require.EqualValues(t, exp.f3, actual.f3) - require.EqualValues(t, exp.f4, actual.f4) - require.EqualValues(t, exp.f5, actual.f5) - require.EqualValues(t, exp.f6, actual.f6) - require.EqualValues(t, exp.f7, actual.f7) - require.EqualValues(t, exp.f8, actual.f8) - require.EqualValues(t, exp.f9, actual.f9) - require.EqualValues(t, exp.f10, actual.f10) - require.EqualValues(t, exp.f11, actual.f11) - require.EqualValues(t, exp.f12, actual.f12) - require.Equal(t, len(exp.f13), len(actual.f13)) -} - -func TestDecode_Errors(t *testing.T) { - var boolVal bool - err := DecodeField(bytes.NewReader([]byte{0x02}), &boolVal) - require.Error(t, err) - require.Contains(t, err.Error(), "invalid boolean value") - - err = DecodeField(bytes.NewReader([]byte{}), uint64(0)) - require.Error(t, err) - require.Contains(t, err.Error(), "can only decode into pointer types") - - var buf bytes.Buffer - require.NoError(t, writeUvarint(&buf, DefaultMaxByteFieldLen+DefaultMaxVariableArrayLen+1)) - var byteArrVal []byte - err = DecodeField(bytes.NewReader(buf.Bytes()), &byteArrVal) - require.Error(t, err) - require.Contains(t, err.Error(), "byte-assignable field length too large to decode") - - var strVal string - // zero out err since the err message is the same - err = nil - err = DecodeField(bytes.NewReader(buf.Bytes()), &strVal) - require.Error(t, err) - require.Contains(t, err.Error(), "byte-assignable field length too large to decode") - - var strArrVal []string - err = DecodeField(bytes.NewReader(buf.Bytes()), &strArrVal) - require.Error(t, err) - require.Contains(t, err.Error(), "variable array field length too large to decode") -} diff --git a/vendor/fnd.localhost/mstream/encode.go b/vendor/fnd.localhost/mstream/encode.go deleted file mode 100644 index 89c443e..0000000 --- a/vendor/fnd.localhost/mstream/encode.go +++ /dev/null @@ -1,128 +0,0 @@ -package mstream - -import ( - "encoding/binary" - "errors" - "fmt" - "io" - "reflect" -) - -var ( - trueWire = []byte{0x01} - falseWire = []byte{0x00} -) - -func EncodeFields(w io.Writer, items ...interface{}) error { - return defaultEncoder.EncodeFields(w, items...) -} - -func EncodeField(w io.Writer, item interface{}) error { - return defaultEncoder.EncodeField(w, item) -} - -func (c *ConfiguredEncoder) EncodeFields(w io.Writer, items ...interface{}) error { - for _, item := range items { - if err := c.EncodeField(w, item); err != nil { - return err - } - } - - return nil -} - -func (c *ConfiguredEncoder) EncodeField(w io.Writer, item interface{}) error { - var err error - switch it := item.(type) { - case Encoder: - err = it.Encode(w) - case bool: - val := falseWire - if it { - val = trueWire - } - _, err = w.Write(val) - case uint8: - _, err = w.Write([]byte{it}) - case uint16: - b := make([]byte, 2, 2) - binary.BigEndian.PutUint16(b, it) - _, err = w.Write(b) - case uint32: - b := make([]byte, 4, 4) - binary.BigEndian.PutUint32(b, it) - _, err = w.Write(b) - case uint64: - b := make([]byte, 8, 8) - binary.BigEndian.PutUint64(b, it) - _, err = w.Write(b) - case []byte: - if uint64(len(it)) > c.MaxByteFieldLen { - return errors.New("byte-assignable field length too large to encode") - } - if err := writeUvarint(w, len(it)); err != nil { - return err - } - _, err = w.Write(it) - case string: - err = c.EncodeField(w, []byte(item.(string))) - case [32]byte: - _, err = w.Write(it[:]) - default: - err = c.encodeReflect(w, item) - } - - return err -} - -func (c *ConfiguredEncoder) encodeReflect(w io.Writer, item interface{}) error { - t := reflect.TypeOf(item) - - canonicalized := canonicalizeWellKnown(t) - if wellKnownEncoders[canonicalized] != nil { - return wellKnownEncoders[canonicalized](w, item) - } - - if t.Kind() == reflect.Array { - itemVal := reflect.ValueOf(item) - if t.Elem().Kind() == reflect.Uint8 { - itemPtr := reflect.New(t) - itemPtr.Elem().Set(itemVal) - _, err := w.Write(itemPtr.Elem().Slice(0, itemVal.Len()).Bytes()) - return err - } - - for i := 0; i < itemVal.Len(); i++ { - if err := c.EncodeField(w, itemVal.Index(i).Interface()); err != nil { - return err - } - } - return nil - } - - if t.Kind() == reflect.Slice { - val := reflect.ValueOf(item) - if val.Len() > c.MaxVariableArrayLen { - return errors.New("variable array field length too large to encode") - } - - if err := writeUvarint(w, val.Len()); err != nil { - return err - } - for i := 0; i < val.Len(); i++ { - if err := c.EncodeField(w, val.Index(i).Interface()); err != nil { - return err - } - } - return nil - } - - return errors.New(fmt.Sprintf("type %s cannot be encoded", t.String())) -} - -func writeUvarint(w io.Writer, n int) error { - lenBuf := make([]byte, binary.MaxVarintLen64, binary.MaxVarintLen64) - bytesWritten := binary.PutUvarint(lenBuf, uint64(n)) - _, err := w.Write(lenBuf[:bytesWritten]) - return err -} diff --git a/vendor/fnd.localhost/mstream/encode_test.go b/vendor/fnd.localhost/mstream/encode_test.go deleted file mode 100644 index bdcee68..0000000 --- a/vendor/fnd.localhost/mstream/encode_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package mstream - -import ( - "bytes" - "encoding/hex" - "math" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestEncodeFields(t *testing.T) { - cafe := new(cafeEncodeDecoder) - - var threeTwoByte [32]byte - threeTwoByte[1] = 0xff - - var buf bytes.Buffer - require.NoError(t, EncodeFields( - &buf, - cafe, - []*cafeEncodeDecoder{ - cafe, - cafe, - }, - true, - false, - uint8(0), - uint16(0), - uint32(0), - uint64(0), - uint8(math.MaxUint8), - uint16(math.MaxUint16), - uint32(math.MaxUint32), - uint64(math.MaxUint64), - threeTwoByte, - [2]string{ - "testing", - "testing", - }, - []byte{ - 0x01, 0x02, - }, - []string{ - "testing", - "testing", - }, - "hello there", - time.Unix(1, 0), - )) - require.Equal( - t, - "cafe"+ - "02cafecafe"+ - "01"+ - "00"+ - "00"+ - "0000"+ - "00000000"+ - "0000000000000000"+ - "ff"+ - "ffff"+ - "ffffffff"+ - "ffffffffffffffff"+ - "00ff000000000000000000000000000000000000000000000000000000000000"+ - "0774657374696e670774657374696e67"+ - "020102"+ - "020774657374696e670774657374696e67"+ - "0b68656c6c6f207468657265"+ - "0000000000000001", - hex.EncodeToString(buf.Bytes()), - ) - - buf.Reset() - err := EncodeFields(&buf, uint8(1), struct{}{}) - require.Error(t, err) - require.Contains(t, err.Error(), "cannot be encoded") -} - -func TestEncode_Errors(t *testing.T) { - rw := new(NopReadWriter) - err := EncodeField(rw, &struct{}{}) - require.Error(t, err) - require.Contains(t, err.Error(), "cannot be encoded") - - customEncoder := &ConfiguredEncoder{ - MaxVariableArrayLen: 5, - MaxByteFieldLen: 5, - } - - err = customEncoder.EncodeField(rw, make([]byte, customEncoder.MaxByteFieldLen+1)) - require.Error(t, err) - require.Contains(t, err.Error(), "byte-assignable field length too large to encode") - - // zero out err since the message is the same - err = nil - err = customEncoder.EncodeField(rw, "123456") - require.Error(t, err) - require.Contains(t, err.Error(), "byte-assignable field length too large to encode") - - err = customEncoder.EncodeField(rw, make([]string, customEncoder.MaxVariableArrayLen+1)) - require.Error(t, err) - require.Contains(t, err.Error(), "variable array field length too large to encode") -} diff --git a/vendor/fnd.localhost/mstream/encoder.go b/vendor/fnd.localhost/mstream/encoder.go deleted file mode 100644 index d1884aa..0000000 --- a/vendor/fnd.localhost/mstream/encoder.go +++ /dev/null @@ -1,31 +0,0 @@ -package mstream - -import "io" - -const ( - DefaultMaxVariableArrayLen = 1024 - DefaultMaxByteFieldLen = 256 * 1024 -) - -type Encoder interface { - Encode(w io.Writer) error -} - -type Decoder interface { - Decode(r io.Reader) error -} - -type EncodeDecoder interface { - Encoder - Decoder -} - -type ConfiguredEncoder struct { - MaxVariableArrayLen int - MaxByteFieldLen uint64 -} - -var defaultEncoder = &ConfiguredEncoder{ - MaxVariableArrayLen: DefaultMaxVariableArrayLen, - MaxByteFieldLen: DefaultMaxByteFieldLen, -} diff --git a/vendor/fnd.localhost/mstream/encoding_bench_test.go b/vendor/fnd.localhost/mstream/encoding_bench_test.go deleted file mode 100644 index 473f428..0000000 --- a/vendor/fnd.localhost/mstream/encoding_bench_test.go +++ /dev/null @@ -1,107 +0,0 @@ -package mstream - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -type NopReadWriter struct{} - -func (n *NopReadWriter) Read(p []byte) (int, error) { - return len(p), nil -} - -func (n *NopReadWriter) Write(p []byte) (int, error) { - return len(p), nil -} - -func BenchmarkUint8Encoding(b *testing.B) { - var i uint8 - rw := new(NopReadWriter) - for n := 0; n < b.N; n++ { - require.NoError(b, EncodeField(rw, i)) - } -} - -func BenchmarkUint16Encoding(b *testing.B) { - var i uint16 - rw := new(NopReadWriter) - for n := 0; n < b.N; n++ { - require.NoError(b, EncodeField(rw, i)) - } -} - -func BenchmarkUint32Encoding(b *testing.B) { - var i uint32 - rw := new(NopReadWriter) - for n := 0; n < b.N; n++ { - require.NoError(b, EncodeField(rw, i)) - } -} - -func BenchmarkUint64Encoding(b *testing.B) { - var i uint64 - rw := new(NopReadWriter) - for n := 0; n < b.N; n++ { - require.NoError(b, EncodeField(rw, i)) - } -} - -func BenchmarkByteSliceEncoding1024(b *testing.B) { - bytes := make([]byte, 1024, 1024) - rw := new(NopReadWriter) - for n := 0; n < b.N; n++ { - require.NoError(b, EncodeField(rw, bytes)) - } -} - -func BenchmarkStringEncoding1024(b *testing.B) { - bytes := make([]byte, 1024, 1024) - str := string(bytes) - rw := new(NopReadWriter) - for n := 0; n < b.N; n++ { - require.NoError(b, EncodeField(rw, str)) - } -} - -func BenchmarkByteArrayEncoding32(b *testing.B) { - bytes := make([]byte, 32, 32) - rw := new(NopReadWriter) - for n := 0; n < b.N; n++ { - require.NoError(b, EncodeField(rw, bytes)) - } -} - -func BenchmarkByteArrayEncodingReflect(b *testing.B) { - var bytes [1024]byte - rw := new(NopReadWriter) - for n := 0; n < b.N; n++ { - require.NoError(b, EncodeField(rw, bytes)) - } -} - -func BenchmarkUint16ArrayEncodingReflect(b *testing.B) { - var uints [1024]uint16 - rw := new(NopReadWriter) - for n := 0; n < b.N; n++ { - require.NoError(b, EncodeField(rw, uints)) - } -} - -func BenchmarkStringArrayEncodingReflect(b *testing.B) { - var strings [1024]string - rw := new(NopReadWriter) - for n := 0; n < b.N; n++ { - require.NoError(b, EncodeField(rw, strings)) - } -} - -func BenchmarkWellKnownTimeEncoding(b *testing.B) { - ts := time.Now() - rw := new(NopReadWriter) - for n := 0; n < b.N; n++ { - require.NoError(b, EncodeField(rw, ts)) - } -} diff --git a/vendor/fnd.localhost/mstream/go.mod b/vendor/fnd.localhost/mstream/go.mod deleted file mode 100644 index df8267d..0000000 --- a/vendor/fnd.localhost/mstream/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module fnd.localhost/mstream - -go 1.12 - -require ( - github.com/pkg/errors v0.9.1 - github.com/stretchr/testify v1.4.0 -) diff --git a/vendor/fnd.localhost/mstream/go.sum b/vendor/fnd.localhost/mstream/go.sum deleted file mode 100644 index 1552a1a..0000000 --- a/vendor/fnd.localhost/mstream/go.sum +++ /dev/null @@ -1,13 +0,0 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/fnd.localhost/mstream/well_known.go b/vendor/fnd.localhost/mstream/well_known.go deleted file mode 100644 index b6969f7..0000000 --- a/vendor/fnd.localhost/mstream/well_known.go +++ /dev/null @@ -1,52 +0,0 @@ -package mstream - -import ( - "fmt" - "io" - "reflect" - "time" - - "github.com/pkg/errors" -) - -type encoderFunc func(w io.Writer, val interface{}) error -type decoderFunc func(r io.Reader, val interface{}) error - -var ( - wellKnownEncoders = make(map[string]encoderFunc) - wellKnownDecoders = make(map[string]decoderFunc) -) - -func EncodeTime(w io.Writer, val interface{}) error { - cast, ok := val.(time.Time) - if !ok { - return errors.New("value is not a time.Time") - } - - return EncodeField(w, uint64(cast.Unix())) -} - -func DecodeTime(r io.Reader, val interface{}) error { - cast, ok := val.(*time.Time) - if !ok { - return errors.New("value is not a *time.Time") - } - - var unixTs uint64 - if err := DecodeField(r, &unixTs); err != nil { - return errors.Wrap(err, "failed to decode timestamp") - } - - *cast = time.Unix(int64(unixTs), 0) - return nil -} - -func canonicalizeWellKnown(t reflect.Type) string { - return fmt.Sprintf("%s/%s", t.PkgPath(), t.Name()) -} - -func init() { - timeTypeKey := canonicalizeWellKnown(reflect.TypeOf(time.Time{})) - wellKnownEncoders[timeTypeKey] = EncodeTime - wellKnownDecoders[timeTypeKey] = DecodeTime -} diff --git a/vendor/fnd.localhost/mstream/well_known_test.go b/vendor/fnd.localhost/mstream/well_known_test.go deleted file mode 100644 index c64993d..0000000 --- a/vendor/fnd.localhost/mstream/well_known_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package mstream - -import ( - "bytes" - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestEncodeTime(t *testing.T) { - var buf bytes.Buffer - require.NoError(t, EncodeTime(&buf, time.Unix(12345, 0))) - require.EqualValues(t, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39}, buf.Bytes()) - - rw := new(NopReadWriter) - err := EncodeTime(rw, 9001) - require.Error(t, err) - require.Contains(t, err.Error(), "value is not a time.Time") -} - -func TestDecodeTime(t *testing.T) { - input := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39} - rd := bytes.NewReader(input) - var output time.Time - require.NoError(t, DecodeTime(rd, &output)) - require.True(t, output.Equal(time.Unix(12345, 0))) - - rd = bytes.NewReader(input) - err := DecodeTime(rd, output) - require.Error(t, err) - require.Contains(t, err.Error(), "value is not a *time.Time") -} diff --git a/wire/blob_req.go b/wire/blob_req.go new file mode 100644 index 0000000..c2709bd --- /dev/null +++ b/wire/blob_req.go @@ -0,0 +1,55 @@ +package wire + +import ( + "io" + + "fnd/crypto" + "fnd.localhost/dwire" +) + +type BlobReq struct { + HashCacher + + Name string + EpochHeight uint16 + SectorSize uint16 +} + +var _ Message = (*BlobReq)(nil) + +func (u *BlobReq) MsgType() MessageType { + return MessageTypeBlobReq +} + +func (u *BlobReq) Equals(other Message) bool { + cast, ok := other.(*BlobReq) + if !ok { + return false + } + + return u.Name == cast.Name && + u.EpochHeight == cast.EpochHeight && + u.SectorSize == cast.SectorSize +} + +func (u *BlobReq) Encode(w io.Writer) error { + return dwire.EncodeFields( + w, + u.Name, + u.EpochHeight, + u.SectorSize, + ) +} + +func (u *BlobReq) Decode(r io.Reader) error { + return dwire.DecodeFields( + r, + &u.Name, + &u.EpochHeight, + &u.SectorSize, + ) +} + +func (u *BlobReq) Hash() (crypto.Hash, error) { + return u.HashCacher.Hash(u) +} diff --git a/wire/blob_req_test.go b/wire/blob_req_test.go new file mode 100644 index 0000000..8dc6f0e --- /dev/null +++ b/wire/blob_req_test.go @@ -0,0 +1,15 @@ +package wire + +import ( + "testing" +) + +func TestBlobReq_Encoding(t *testing.T) { + blobReq := &BlobReq{ + Name: "testname.", + EpochHeight: 0, + SectorSize: 1, + } + + testMessageEncoding(t, "blob_req", blobReq, &BlobReq{}) +} diff --git a/wire/blob_res.go b/wire/blob_res.go new file mode 100644 index 0000000..16371bc --- /dev/null +++ b/wire/blob_res.go @@ -0,0 +1,72 @@ +package wire + +import ( + "io" + + "fnd/blob" + "fnd/crypto" + + "fnd.localhost/dwire" +) + +type BlobRes struct { + HashCacher + + Name string + EpochHeight uint16 + PayloadPosition uint16 + PrevHash crypto.Hash + ReservedRoot crypto.Hash + Payload []blob.Sector + Signature crypto.Signature +} + +var _ Message = (*BlobRes)(nil) + +func (s *BlobRes) MsgType() MessageType { + return MessageTypeBlobRes +} + +func (s *BlobRes) Equals(other Message) bool { + cast, ok := other.(*BlobRes) + if !ok { + return false + } + + return s.Name == cast.Name && + s.EpochHeight == cast.EpochHeight && + s.PayloadPosition == cast.PayloadPosition && + s.PrevHash == cast.PrevHash && + s.ReservedRoot == cast.ReservedRoot && + s.Signature == cast.Signature +} + +func (s *BlobRes) Encode(w io.Writer) error { + return dwire.EncodeFields( + w, + s.Name, + s.EpochHeight, + s.PayloadPosition, + s.PrevHash, + s.ReservedRoot, + s.Payload, + s.Signature, + ) +} + +func (s *BlobRes) Decode(r io.Reader) error { + return dwire.DecodeFields( + r, + &s.Name, + &s.EpochHeight, + &s.PayloadPosition, + &s.PrevHash, + &s.ReservedRoot, + &s.Payload, + &s.Signature, + ) +} + +func (s *BlobRes) Hash() (crypto.Hash, error) { + return s.HashCacher.Hash(s) +} diff --git a/wire/blob_res_test.go b/wire/blob_res_test.go new file mode 100644 index 0000000..3b1ae78 --- /dev/null +++ b/wire/blob_res_test.go @@ -0,0 +1,19 @@ +package wire + +import ( + "testing" +) + +func TestBlobRes_Encoding(t *testing.T) { + blobRes := &BlobRes{ + Name: "testname.", + EpochHeight: 0, + PayloadPosition: 0, + PrevHash: fixedHash, + ReservedRoot: fixedHash, + Payload: nil, + Signature: fixedSig, + } + + testMessageEncoding(t, "blob_res", blobRes, &BlobRes{}) +} diff --git a/wire/envelope.go b/wire/envelope.go index e0480fa..c074c4c 100644 --- a/wire/envelope.go +++ b/wire/envelope.go @@ -4,9 +4,10 @@ import ( "bytes" "fmt" "fnd/crypto" - "fnd.localhost/dwire" "io" "time" + + "fnd.localhost/dwire" ) type Envelope struct { @@ -82,20 +83,18 @@ func (e *Envelope) Decode(r io.Reader) error { msg = &Update{} case MessageTypeNilUpdate: msg = &NilUpdate{} - case MessageTypeTreeBaseReq: - msg = &TreeBaseReq{} - case MessageTypeTreeBaseRes: - msg = &TreeBaseRes{} - case MessageTypeSectorReq: - msg = &SectorReq{} - case MessageTypeSectorRes: - msg = &SectorRes{} + case MessageTypeBlobReq: + msg = &BlobReq{} + case MessageTypeBlobRes: + msg = &BlobRes{} case MessageTypePeerReq: msg = &PeerReq{} case MessageTypePeerRes: msg = &PeerRes{} case MessageTypeUpdateReq: msg = &UpdateReq{} + case MessageTypeEquivocationProof: + msg = &EquivocationProof{} default: return fmt.Errorf("invalid message type: %d", e.MessageType) } diff --git a/wire/equivocation_proof.go b/wire/equivocation_proof.go new file mode 100644 index 0000000..2f9f042 --- /dev/null +++ b/wire/equivocation_proof.go @@ -0,0 +1,94 @@ +package wire + +import ( + "io" + + "fnd/blob" + "fnd/crypto" + + "fnd.localhost/dwire" +) + +type EquivocationProof struct { + HashCacher + + Name string + + RemoteEpochHeight uint16 + RemotePayloadPosition uint16 + RemotePrevHash crypto.Hash + RemoteReservedRoot crypto.Hash + RemotePayload []blob.Sector + RemoteSignature crypto.Signature + + LocalEpochHeight uint16 + LocalSectorSize uint16 + LocalSectorTipHash crypto.Hash + LocalReservedRoot crypto.Hash + LocalSignature crypto.Signature +} + +var _ Message = (*EquivocationProof)(nil) + +func (s *EquivocationProof) MsgType() MessageType { + return MessageTypeEquivocationProof +} + +func (s *EquivocationProof) Equals(other Message) bool { + cast, ok := other.(*EquivocationProof) + if !ok { + return false + } + + return s.Name == cast.Name && + s.RemoteEpochHeight == cast.RemoteEpochHeight && + s.RemotePayloadPosition == cast.RemotePayloadPosition && + s.RemotePrevHash == cast.RemotePrevHash && + s.RemoteReservedRoot == cast.RemoteReservedRoot && + s.RemoteSignature == cast.RemoteSignature && + s.LocalEpochHeight == cast.LocalEpochHeight && + s.LocalSectorSize == cast.LocalSectorSize && + s.LocalSectorTipHash == cast.LocalSectorTipHash && + s.LocalReservedRoot == cast.LocalReservedRoot && + s.LocalSignature == cast.LocalSignature +} + +func (s *EquivocationProof) Encode(w io.Writer) error { + return dwire.EncodeFields( + w, + s.Name, + s.RemoteEpochHeight, + s.RemotePayloadPosition, + s.RemotePrevHash, + s.RemoteReservedRoot, + s.RemotePayload, + s.RemoteSignature, + s.LocalEpochHeight, + s.LocalSectorSize, + s.LocalSectorTipHash, + s.LocalReservedRoot, + s.LocalSignature, + ) +} + +func (s *EquivocationProof) Decode(r io.Reader) error { + return dwire.DecodeFields( + r, + &s.Name, + &s.RemoteEpochHeight, + &s.RemotePayloadPosition, + &s.RemotePrevHash, + &s.RemoteReservedRoot, + &s.RemotePayload, + &s.RemoteSignature, + &s.LocalEpochHeight, + &s.LocalSectorSize, + &s.LocalSectorTipHash, + &s.LocalReservedRoot, + &s.LocalSignature, + ) +} + +func (s *EquivocationProof) Hash() (crypto.Hash, error) { + return s.HashCacher.Hash(s) +} diff --git a/wire/message.go b/wire/message.go index bfde66d..52d4c6f 100644 --- a/wire/message.go +++ b/wire/message.go @@ -2,8 +2,9 @@ package wire import ( "fnd/crypto" - "fnd.localhost/dwire" "io" + + "fnd.localhost/dwire" ) type Message interface { @@ -21,14 +22,13 @@ const ( MessageTypePing MessageTypeUpdate MessageTypeNilUpdate - MessageTypeTreeBaseReq - MessageTypeTreeBaseRes - MessageTypeSectorReq - MessageTypeSectorRes + MessageTypeBlobReq + MessageTypeBlobRes MessageTypePeerReq MessageTypePeerRes MessageTypeUpdateReq MessageTypeNameRes + MessageTypeEquivocationProof ) func (t MessageType) String() string { @@ -43,14 +43,10 @@ func (t MessageType) String() string { return "Update" case MessageTypeNilUpdate: return "NilUpdate" - case MessageTypeTreeBaseReq: - return "TreeBaseReq" - case MessageTypeTreeBaseRes: - return "TreeBaseRes" - case MessageTypeSectorReq: - return "SectorReq" - case MessageTypeSectorRes: - return "SectorRes" + case MessageTypeBlobReq: + return "BlobReq" + case MessageTypeBlobRes: + return "BlobRes" case MessageTypePeerReq: return "PeerReq" case MessageTypePeerRes: @@ -59,6 +55,8 @@ func (t MessageType) String() string { return "UpdateReq" case MessageTypeNameRes: return "NameRes" + case MessageTypeEquivocationProof: + return "EquivocationProof" default: return "unknown" } diff --git a/wire/sector_req.go b/wire/sector_req.go deleted file mode 100644 index a43b910..0000000 --- a/wire/sector_req.go +++ /dev/null @@ -1,50 +0,0 @@ -package wire - -import ( - "fnd/crypto" - "fnd.localhost/dwire" - "io" -) - -type SectorReq struct { - HashCacher - - Name string - SectorID uint8 -} - -var _ Message = (*SectorReq)(nil) - -func (s *SectorReq) MsgType() MessageType { - return MessageTypeSectorReq -} - -func (s *SectorReq) Equals(other Message) bool { - cast, ok := other.(*SectorReq) - if !ok { - return false - } - - return s.Name == cast.Name && - s.SectorID == cast.SectorID -} - -func (s *SectorReq) Encode(w io.Writer) error { - return dwire.EncodeFields( - w, - s.Name, - s.SectorID, - ) -} - -func (s *SectorReq) Decode(r io.Reader) error { - return dwire.DecodeFields( - r, - &s.Name, - &s.SectorID, - ) -} - -func (s *SectorReq) Hash() (crypto.Hash, error) { - return s.HashCacher.Hash(s) -} diff --git a/wire/sector_req_test.go b/wire/sector_req_test.go deleted file mode 100644 index f8d055b..0000000 --- a/wire/sector_req_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package wire - -import ( - "testing" -) - -func TestSectorReq_Encoding(t *testing.T) { - sectorReq := &SectorReq{ - Name: "testname.", - SectorID: 16, - } - - testMessageEncoding(t, "sector_req", sectorReq, &SectorReq{}) -} diff --git a/wire/sector_res.go b/wire/sector_res.go deleted file mode 100644 index 75dcca7..0000000 --- a/wire/sector_res.go +++ /dev/null @@ -1,55 +0,0 @@ -package wire - -import ( - "fnd/blob" - "fnd/crypto" - "fnd.localhost/dwire" - "io" -) - -type SectorRes struct { - HashCacher - - Name string - SectorID uint8 - Sector blob.Sector -} - -var _ Message = (*SectorRes)(nil) - -func (s *SectorRes) MsgType() MessageType { - return MessageTypeSectorRes -} - -func (s *SectorRes) Equals(other Message) bool { - cast, ok := other.(*SectorRes) - if !ok { - return false - } - - return s.Name == cast.Name && - s.SectorID == cast.SectorID && - s.Sector == cast.Sector -} - -func (s *SectorRes) Encode(w io.Writer) error { - return dwire.EncodeFields( - w, - s.Name, - s.SectorID, - s.Sector, - ) -} - -func (s *SectorRes) Decode(r io.Reader) error { - return dwire.DecodeFields( - r, - &s.Name, - &s.SectorID, - &s.Sector, - ) -} - -func (s *SectorRes) Hash() (crypto.Hash, error) { - return s.HashCacher.Hash(s) -} diff --git a/wire/sector_res_test.go b/wire/sector_res_test.go deleted file mode 100644 index d7457ee..0000000 --- a/wire/sector_res_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package wire - -import ( - "fnd/blob" - "testing" -) - -func TestSectorRes_Encoding(t *testing.T) { - sectorRes := &SectorRes{ - Name: "testname.", - SectorID: 16, - Sector: blob.Sector{}, - } - - testMessageEncoding(t, "sector_res", sectorRes, &SectorRes{}) -} diff --git a/wire/testdata/blob_req b/wire/testdata/blob_req new file mode 100644 index 0000000..e0d6066 Binary files /dev/null and b/wire/testdata/blob_req differ diff --git a/wire/testdata/blob_res b/wire/testdata/blob_res new file mode 100644 index 0000000..1726ca6 Binary files /dev/null and b/wire/testdata/blob_res differ diff --git a/wire/testdata/hello b/wire/testdata/hello index a238b55..a6e5d4f 100644 Binary files a/wire/testdata/hello and b/wire/testdata/hello differ diff --git a/wire/testdata/hello_ack b/wire/testdata/hello_ack index c952a9f..ed43e93 100644 Binary files a/wire/testdata/hello_ack and b/wire/testdata/hello_ack differ diff --git a/wire/testdata/nil_update b/wire/testdata/nil_update index 8554269..197ca51 100644 --- a/wire/testdata/nil_update +++ b/wire/testdata/nil_update @@ -1 +1 @@ - testname. + testname. \ No newline at end of file diff --git a/wire/testdata/peer_res b/wire/testdata/peer_res index 6a5836c..20d344a 100644 Binary files a/wire/testdata/peer_res and b/wire/testdata/peer_res differ diff --git a/wire/testdata/update b/wire/testdata/update index c1df062..ba2bf12 100755 Binary files a/wire/testdata/update and b/wire/testdata/update differ diff --git a/wire/testdata/update_req b/wire/testdata/update_req index 91e240d..ba2bf12 100644 Binary files a/wire/testdata/update_req and b/wire/testdata/update_req differ diff --git a/wire/tree_base_req.go b/wire/tree_base_req.go deleted file mode 100644 index 64200ad..0000000 --- a/wire/tree_base_req.go +++ /dev/null @@ -1,46 +0,0 @@ -package wire - -import ( - "fnd/crypto" - "fnd.localhost/dwire" - "io" -) - -type TreeBaseReq struct { - HashCacher - - Name string -} - -var _ Message = (*TreeBaseReq)(nil) - -func (d *TreeBaseReq) MsgType() MessageType { - return MessageTypeTreeBaseReq -} - -func (d *TreeBaseReq) Equals(other Message) bool { - cast, ok := other.(*TreeBaseReq) - if !ok { - return false - } - - return d.Name == cast.Name -} - -func (d *TreeBaseReq) Encode(w io.Writer) error { - return dwire.EncodeFields( - w, - d.Name, - ) -} - -func (d *TreeBaseReq) Decode(r io.Reader) error { - return dwire.DecodeFields( - r, - &d.Name, - ) -} - -func (d *TreeBaseReq) Hash() (crypto.Hash, error) { - return d.HashCacher.Hash(d) -} diff --git a/wire/tree_base_req_test.go b/wire/tree_base_req_test.go deleted file mode 100644 index 76d167f..0000000 --- a/wire/tree_base_req_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package wire - -import ( - "testing" -) - -func TestTreeBaseReq_Encoding(t *testing.T) { - treeBaseReq := &TreeBaseReq{ - Name: "testname.", - } - - testMessageEncoding(t, "tree_base_req", treeBaseReq, &TreeBaseReq{}) -} diff --git a/wire/tree_base_res.go b/wire/tree_base_res.go deleted file mode 100644 index 3f76676..0000000 --- a/wire/tree_base_res.go +++ /dev/null @@ -1,49 +0,0 @@ -package wire - -import ( - "fnd/blob" - "fnd/crypto" - "fnd.localhost/dwire" - "io" -) - -type TreeBaseRes struct { - HashCacher - - Name string - MerkleBase blob.MerkleBase -} - -func (d *TreeBaseRes) MsgType() MessageType { - return MessageTypeTreeBaseRes -} - -func (d *TreeBaseRes) Equals(other Message) bool { - cast, ok := other.(*TreeBaseRes) - if !ok { - return false - } - - return d.Name == cast.Name && - d.MerkleBase == cast.MerkleBase -} - -func (d *TreeBaseRes) Encode(w io.Writer) error { - return dwire.EncodeFields( - w, - d.Name, - d.MerkleBase, - ) -} - -func (d *TreeBaseRes) Decode(r io.Reader) error { - return dwire.DecodeFields( - r, - &d.Name, - &d.MerkleBase, - ) -} - -func (d *TreeBaseRes) Hash() (crypto.Hash, error) { - return d.HashCacher.Hash(d) -} diff --git a/wire/tree_base_res_test.go b/wire/tree_base_res_test.go deleted file mode 100644 index 66e5eea..0000000 --- a/wire/tree_base_res_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package wire - -import ( - "fnd/blob" - "testing" -) - -func TestTreeBaseRes_Encoding(t *testing.T) { - treeBaseRes := &TreeBaseRes{ - Name: "testname.", - MerkleBase: blob.ZeroMerkleBase, - } - - testMessageEncoding(t, "tree_base_res", treeBaseRes, &TreeBaseRes{}) -} diff --git a/wire/update.go b/wire/update.go index d328a28..2cf670e 100644 --- a/wire/update.go +++ b/wire/update.go @@ -2,19 +2,17 @@ package wire import ( "fnd/crypto" - "fnd.localhost/dwire" "io" - "time" + + "fnd.localhost/dwire" ) type Update struct { HashCacher - Name string - Timestamp time.Time - MerkleRoot crypto.Hash - ReservedRoot crypto.Hash - Signature crypto.Signature + Name string + EpochHeight uint16 + SectorSize uint16 } var _ Message = (*Update)(nil) @@ -30,20 +28,16 @@ func (u *Update) Equals(other Message) bool { } return u.Name == cast.Name && - u.Timestamp.Equal(cast.Timestamp) && - u.MerkleRoot == cast.MerkleRoot && - u.ReservedRoot == cast.ReservedRoot && - u.Signature == cast.Signature + u.EpochHeight == cast.EpochHeight && + u.SectorSize == cast.SectorSize } func (u *Update) Encode(w io.Writer) error { return dwire.EncodeFields( w, u.Name, - u.Timestamp, - u.MerkleRoot, - u.ReservedRoot, - u.Signature, + u.EpochHeight, + u.SectorSize, ) } @@ -51,10 +45,8 @@ func (u *Update) Decode(r io.Reader) error { return dwire.DecodeFields( r, &u.Name, - &u.Timestamp, - &u.MerkleRoot, - &u.ReservedRoot, - &u.Signature, + &u.EpochHeight, + &u.SectorSize, ) } diff --git a/wire/update_req.go b/wire/update_req.go index 7c263d2..2ffbdb5 100644 --- a/wire/update_req.go +++ b/wire/update_req.go @@ -4,14 +4,14 @@ import ( "fnd/crypto" "fnd.localhost/dwire" "io" - "time" ) type UpdateReq struct { HashCacher - Name string - Timestamp time.Time + Name string + EpochHeight uint16 + SectorSize uint16 } var _ Message = (*UpdateReq)(nil) @@ -27,14 +27,16 @@ func (n *UpdateReq) Equals(other Message) bool { } return n.Name == cast.Name && - n.Timestamp.Unix() == cast.Timestamp.Unix() + n.EpochHeight == cast.EpochHeight && + n.SectorSize == cast.SectorSize } func (n *UpdateReq) Encode(w io.Writer) error { return dwire.EncodeFields( w, n.Name, - n.Timestamp, + n.EpochHeight, + n.SectorSize, ) } @@ -42,7 +44,8 @@ func (n *UpdateReq) Decode(r io.Reader) error { return dwire.DecodeFields( r, &n.Name, - &n.Timestamp, + &n.EpochHeight, + &n.SectorSize, ) } diff --git a/wire/update_req_test.go b/wire/update_req_test.go index f98cbcc..2ef6738 100644 --- a/wire/update_req_test.go +++ b/wire/update_req_test.go @@ -2,13 +2,13 @@ package wire import ( "testing" - "time" ) func TestUpdateReq_Encoding(t *testing.T) { updateReq := &UpdateReq{ - Name: "testname", - Timestamp: time.Unix(1234567, 0), + Name: "testname", + EpochHeight: 0, + SectorSize: 0, } testMessageEncoding(t, "update_req", updateReq, &UpdateReq{}) diff --git a/wire/update_test.go b/wire/update_test.go index 23e3bf5..5b49bff 100644 --- a/wire/update_test.go +++ b/wire/update_test.go @@ -6,11 +6,9 @@ import ( func TestUpdate_Encoding(t *testing.T) { update := &Update{ - Name: "testname", - Timestamp: fixedTime, - MerkleRoot: fixedHash, - ReservedRoot: fixedHash, - Signature: fixedSig, + Name: "testname", + EpochHeight: fixedEpochHeight, + SectorSize: fixedSectorSize, } testMessageEncoding(t, "update", update, &Update{}) diff --git a/wire/util_test.go b/wire/util_test.go index d611171..40150d9 100644 --- a/wire/util_test.go +++ b/wire/util_test.go @@ -3,19 +3,17 @@ package wire import ( "bytes" "fmt" - "fnd/blob" "fnd/crypto" "github.com/stretchr/testify/require" "io/ioutil" "testing" - "time" ) var ( fixedSig crypto.Signature fixedHash crypto.Hash - fixedMerkleProof blob.MerkleProof - fixedTime = time.Unix(1234567890, 0) + fixedEpochHeight = uint16(0) + fixedSectorSize = uint16(0) ) func testMessageEncoding(t *testing.T, fixtureName string, input Message, proto interface{}) { @@ -47,5 +45,4 @@ func testMessageEncoding(t *testing.T, fixtureName string, input Message, proto func init() { fixedSig[1] = 0xff fixedHash[1] = 0xff - fixedMerkleProof[1] = 0xff }