ipld-eth-server/pkg/ipfs/ipld/btc_header.go

203 lines
4.6 KiB
Go

// VulcanizeDB
// Copyright © 2019 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package ipld
import (
"bytes"
"encoding/hex"
"fmt"
"github.com/btcsuite/btcd/wire"
"github.com/ipfs/go-cid"
node "github.com/ipfs/go-ipld-format"
mh "github.com/multiformats/go-multihash"
)
type BtcHeader struct {
*wire.BlockHeader
rawdata []byte
cid cid.Cid
}
// Static (compile time) check that BtcBtcHeader satisfies the node.Node interface.
var _ node.Node = (*BtcHeader)(nil)
/*
INPUT
*/
// NewBtcHeader converts a *wire.Header into an BtcHeader IPLD node
func NewBtcHeader(header *wire.BlockHeader) (*BtcHeader, error) {
w := bytes.NewBuffer(make([]byte, 0, 80))
if err := header.Serialize(w); err != nil {
return nil, err
}
rawdata := w.Bytes()
c, err := rawdataToCid(MBitcoinHeader, rawdata, mh.DBL_SHA2_256)
if err != nil {
return nil, err
}
return &BtcHeader{
BlockHeader: header,
cid: c,
rawdata: rawdata,
}, nil
}
/*
OUTPUT
*/
// DecodeBtcHeader takes a cid and its raw binary data
// from IPFS and returns an BtcHeader object for further processing.
func DecodeBtcHeader(c cid.Cid, b []byte) (*BtcHeader, error) {
var h *wire.BlockHeader
w := bytes.NewBuffer(b)
if err := h.Deserialize(w); err != nil {
return nil, err
}
return &BtcHeader{
BlockHeader: h,
cid: c,
rawdata: b,
}, nil
}
/*
Block INTERFACE
*/
func (b *BtcHeader) Cid() cid.Cid {
return b.cid
}
func (b *BtcHeader) RawData() []byte {
return b.rawdata
}
func (b *BtcHeader) String() string {
return fmt.Sprintf("<BtcHeader %s>", b.cid)
}
func (b *BtcHeader) Loggable() map[string]interface{} {
// TODO: more helpful info here
return map[string]interface{}{
"type": "bitcoin_block",
}
}
/*
Node INTERFACE
*/
func (b *BtcHeader) Links() []*node.Link {
return []*node.Link{
{
Name: "tx",
Cid: sha256ToCid(MBitcoinTx, b.MerkleRoot.CloneBytes()),
},
{
Name: "parent",
Cid: sha256ToCid(MBitcoinHeader, b.PrevBlock.CloneBytes()),
},
}
}
// Resolve attempts to traverse a path through this block.
func (b *BtcHeader) Resolve(path []string) (interface{}, []string, error) {
if len(path) == 0 {
return nil, nil, fmt.Errorf("zero length path")
}
switch path[0] {
case "version":
return b.Version, path[1:], nil
case "timestamp":
return b.Timestamp, path[1:], nil
case "bits":
return b.Bits, path[1:], nil
case "nonce":
return b.Nonce, path[1:], nil
case "parent":
return &node.Link{Cid: sha256ToCid(MBitcoinHeader, b.PrevBlock.CloneBytes())}, path[1:], nil
case "tx":
return &node.Link{Cid: sha256ToCid(MBitcoinTx, b.MerkleRoot.CloneBytes())}, path[1:], nil
default:
return nil, nil, fmt.Errorf("no such link")
}
}
// ResolveLink is a helper function that allows easier traversal of links through blocks
func (b *BtcHeader) ResolveLink(path []string) (*node.Link, []string, error) {
out, rest, err := b.Resolve(path)
if err != nil {
return nil, nil, err
}
lnk, ok := out.(*node.Link)
if !ok {
return nil, nil, fmt.Errorf("object at path was not a link")
}
return lnk, rest, nil
}
func cidToHash(c cid.Cid) []byte {
h := []byte(c.Hash())
return h[len(h)-32:]
}
func hashToCid(hv []byte, t uint64) cid.Cid {
h, _ := mh.Encode(hv, mh.DBL_SHA2_256)
return cid.NewCidV1(t, h)
}
func (b *BtcHeader) Size() (uint64, error) {
return uint64(len(b.rawdata)), nil
}
func (b *BtcHeader) Stat() (*node.NodeStat, error) {
return &node.NodeStat{}, nil
}
func (b *BtcHeader) Tree(p string, depth int) []string {
// TODO: this isnt a correct implementation yet
return []string{"difficulty", "nonce", "version", "timestamp", "tx", "parent"}
}
func (b *BtcHeader) BTCSha() []byte {
blkmh, _ := mh.Sum(b.rawdata, mh.DBL_SHA2_256, -1)
return blkmh[2:]
}
func (b *BtcHeader) HexHash() string {
return hex.EncodeToString(revString(b.BTCSha()))
}
func (b *BtcHeader) Copy() node.Node {
nb := *b // cheating shallow copy
return &nb
}
func revString(s []byte) []byte {
b := make([]byte, len(s))
for i, v := range []byte(s) {
b[len(b)-(i+1)] = v
}
return b
}