merge in lbryschema.go

This commit is contained in:
Alex Grintsvayg 2020-09-01 13:31:15 -04:00
commit a16797cc53
No known key found for this signature in database
GPG key ID: AEB3F089F86A22B5
29 changed files with 1974 additions and 0 deletions

4
schema/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.idea/
lbryschema-cli
lbryschema-python-binding.h
lbryschema-python-binding.so

9
schema/.travis.yml Normal file
View file

@ -0,0 +1,9 @@
os: linux
dist: trusty
language: go
go:
- 1.10.3
script:
- ./build_and_test.sh

View file

@ -0,0 +1,29 @@
package address
import "testing"
func TestDecodeAddressLBRYCrdMain(t *testing.T) {
addr := "bUc9gyCJPKu2CBYpTvJ98MdmsLb68utjP6"
correct := [25]byte{85, 174, 41, 64, 245, 110, 91, 239, 43, 208, 32, 73, 115, 20, 70, 204, 83, 199, 3,
206, 210, 176, 194, 188, 193}
result, err := DecodeAddress(addr, "lbrycrd_main")
if err != nil {
t.Error(err)
}
if result != correct {
t.Error("Mismatch")
}
}
func TestEncodeAddressLBRYCrdMain(t *testing.T) {
addr := [25]byte{85, 174, 41, 64, 245, 110, 91, 239, 43, 208, 32, 73, 115, 20, 70, 204, 83, 199, 3,
206, 210, 176, 194, 188, 193}
correct := "bUc9gyCJPKu2CBYpTvJ98MdmsLb68utjP6"
result, err := EncodeAddress(addr, "lbrycrd_main")
if err != nil {
t.Error(err)
}
if result != correct {
t.Error("Mismatch")
}
}

View file

@ -0,0 +1 @@
package base58

View file

@ -0,0 +1,21 @@
package base58
import (
"errors"
"math/big"
)
var b58Characters = [58]byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45,
0x46, 0x47, 0x48, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7a}
func CharacterIndex(character byte) (*big.Int, error) {
for i := 0; i < 58; i++ {
if b58Characters[i] == character {
return big.NewInt(int64(i)), nil
}
}
return nil, errors.New("invalid character")
}

View file

@ -0,0 +1,20 @@
package base58
import "crypto/sha256"
const checksumLength = 4
func VerifyBase58Checksum(v []byte) bool {
checksum := [checksumLength]byte{}
for i := range checksum {
checksum[i] = v[len(v)-checksumLength+i]
}
real_checksum := sha256.Sum256(v[:len(v)-checksumLength])
real_checksum = sha256.Sum256(real_checksum[:])
for i, c := range checksum {
if c != real_checksum[i] {
return false
}
}
return true
}

View file

@ -0,0 +1,39 @@
package base58
import (
"errors"
"math/big"
)
func DecodeBase58(value string, size int64) ([]byte, error) {
buf := []byte(value)
longValue := big.NewInt(0)
result := make([]byte, size)
for i := int64(len(buf) - 1); i >= 0; i-- {
to_add := big.NewInt(0)
to_add = to_add.Exp(big.NewInt(58), big.NewInt(i), to_add)
c, err := CharacterIndex(buf[int64(len(buf))-i-1])
if err != nil {
return result, err
}
to_add = to_add.Mul(c, to_add)
longValue = longValue.Add(to_add, longValue)
}
for i := size - 1; i >= 0; i-- {
m := big.NewInt(0)
longValue, m = longValue.DivMod(longValue, big.NewInt(256), m)
bs := m.Bytes()
if len(bs) == 0 {
bs = append(bs, 0x00)
}
b := byte(bs[0])
result[i] = b
}
if longValue.Int64() != 0 {
return result, errors.New("cannot decode to the given size")
}
if size != int64(len(result)) {
return result, errors.New("length mismatch")
}
return result, nil
}

View file

@ -0,0 +1,32 @@
package base58
import (
"math/big"
)
func EncodeBase58(data []byte) string {
longValue := big.NewInt(0)
result := ""
for i := 0; i < len(data); i++ {
to_add := big.NewInt(0)
to_add = to_add.Exp(big.NewInt(256), big.NewInt(int64(i)), to_add)
to_add = to_add.Mul(big.NewInt(int64(data[24-i])), to_add)
longValue = longValue.Add(to_add, longValue)
}
i := 0
for {
m := big.NewInt(0)
longValue, m = longValue.DivMod(longValue, big.NewInt(58), m)
bs := m.Bytes()
if len(bs) == 0 {
bs = append(bs, 0x00)
}
b := b58Characters[bs[0]]
result = string(b) + result
if longValue.Int64() == 0 {
break
}
i += 1
}
return result
}

19
schema/address/decode.go Normal file
View file

@ -0,0 +1,19 @@
package address
import (
"github.com/lbryio/lbry.go/extras/errors"
"github.com/lbryio/lbryschema.go/address/base58"
)
func DecodeAddress(address string, blockchainName string) ([addressLength]byte, error) {
decoded, err := base58.DecodeBase58(address, addressLength)
if err != nil {
return [addressLength]byte{}, errors.Err("failed to decode")
}
buf := [addressLength]byte{}
for i, b := range decoded {
buf[i] = b
}
return ValidateAddress(buf, blockchainName)
}

13
schema/address/encode.go Normal file
View file

@ -0,0 +1,13 @@
package address
import (
"github.com/lbryio/lbryschema.go/address/base58"
)
func EncodeAddress(address [addressLength]byte, blockchainName string) (string, error) {
buf, err := ValidateAddress(address, blockchainName)
if err != nil {
return "", err
}
return base58.EncodeBase58(buf[:]), nil
}

View file

@ -0,0 +1,69 @@
package address
import (
"github.com/lbryio/lbry.go/extras/errors"
"github.com/lbryio/lbryschema.go/address/base58"
)
const lbrycrdMainPubkeyPrefix = byte(85)
const lbrycrdMainScriptPrefix = byte(122)
const lbrycrdTestnetPubkeyPrefix = byte(111)
const lbrycrdTestnetScriptPrefix = byte(196)
const lbrycrdRegtestPubkeyPrefix = byte(111)
const lbrycrdRegtestScriptPrefix = byte(196)
const prefixLength = 1
const pubkeyLength = 20
const checksumLength = 4
const addressLength = prefixLength + pubkeyLength + checksumLength
const lbrycrdMain = "lbrycrd_main"
const lbrycrdTestnet = "lbrycrd_testnet"
const lbrycrdRegtest = "lbrycrd_regtest"
var addressPrefixes = map[string][2]byte{
lbrycrdMain: [2]byte{lbrycrdMainPubkeyPrefix, lbrycrdMainScriptPrefix},
lbrycrdTestnet: [2]byte{lbrycrdTestnetPubkeyPrefix, lbrycrdTestnetScriptPrefix},
lbrycrdRegtest: [2]byte{lbrycrdRegtestPubkeyPrefix, lbrycrdRegtestScriptPrefix},
}
func PrefixIsValid(address [addressLength]byte, blockchainName string) bool {
prefix := address[0]
for _, addrPrefix := range addressPrefixes[blockchainName] {
if addrPrefix == prefix {
return true
}
}
return false
}
func PubKeyIsValid(address [addressLength]byte) bool {
pubkey := address[prefixLength : pubkeyLength+prefixLength]
// TODO: validate this for real
if len(pubkey) != pubkeyLength {
return false
}
return true
}
func ChecksumIsValid(address [addressLength]byte) bool {
return base58.VerifyBase58Checksum(address[:])
}
func ValidateAddress(address [addressLength]byte, blockchainName string) ([addressLength]byte, error) {
if blockchainName != lbrycrdMain && blockchainName != lbrycrdTestnet && blockchainName != lbrycrdRegtest {
return address, errors.Err("invalid blockchain name")
}
if !PrefixIsValid(address, blockchainName) {
return address, errors.Err("invalid prefix")
}
if !PubKeyIsValid(address) {
return address, errors.Err("invalid pubkey")
}
if !ChecksumIsValid(address) {
return address, errors.Err("invalid address checksum")
}
return address, nil
}

5
schema/build.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/bash
set -euxo pipefail
go build ./...
go build ./cli/lbryschema-cli.go

5
schema/build_and_test.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/bash
set -euxo pipefail
./build.sh
./test.sh

249
schema/claim/claim.go Normal file
View file

@ -0,0 +1,249 @@
package claim
import (
"encoding/hex"
"strconv"
"github.com/lbryio/lbry.go/extras/errors"
"github.com/lbryio/lbryschema.go/address"
legacy_pb "github.com/lbryio/types/v1/go"
pb "github.com/lbryio/types/v2/go"
"github.com/golang/protobuf/proto"
)
type version byte
func (v version) byte() byte {
return byte(v)
}
const (
NoSig = version(byte(0))
//Signature using ECDSA SECP256k1 key and SHA-256 hash.
WithSig = version(byte(1))
UNKNOWN = version(byte(2))
)
type ClaimHelper struct {
*pb.Claim
LegacyClaim *legacy_pb.Claim
ClaimID []byte
Version version
Signature []byte
Payload []byte
}
const migrationErrorMessage = "migration from v1 to v2 protobuf failed with: "
func (c *ClaimHelper) ValidateAddresses(blockchainName string) error {
if c.Claim != nil { // V2
// check the validity of a fee address
if c.Claim.GetStream() != nil {
fee := c.GetStream().GetFee()
if fee != nil {
return validateAddress(fee.GetAddress(), blockchainName)
} else {
return nil
}
} else if c.GetChannel() != nil {
return nil
}
}
return errors.Err("claim helper created with migrated v2 protobuf claim 'invalid state'")
}
func validateAddress(tmp_addr []byte, blockchainName string) error {
if len(tmp_addr) != 25 {
return errors.Err("invalid address length: " + strconv.FormatInt(int64(len(tmp_addr)), 10) + "!")
}
addr := [25]byte{}
for i := range addr {
addr[i] = tmp_addr[i]
}
_, err := address.EncodeAddress(addr, blockchainName)
if err != nil {
return errors.Err(err)
}
return nil
}
func getVersionFromByte(versionByte byte) version {
if versionByte == byte(0) {
return NoSig
} else if versionByte == byte(1) {
return WithSig
}
return UNKNOWN
}
func (c *ClaimHelper) ValidateCertificate() error {
if c.GetChannel() == nil {
return nil
}
_, err := c.GetPublicKey()
if err != nil {
return errors.Err(err)
}
return nil
}
func (c *ClaimHelper) LoadFromBytes(raw_claim []byte, blockchainName string) error {
if c.String() != "" {
return errors.Err("already initialized")
}
if len(raw_claim) < 1 {
return errors.Err("there is nothing to decode")
}
var claim_pb *pb.Claim
var legacy_claim_pb *legacy_pb.Claim
version := getVersionFromByte(raw_claim[0]) //First byte = version
pbPayload := raw_claim[1:]
var claimID []byte
var signature []byte
if version == WithSig {
if len(raw_claim) < 86 {
return errors.Err("signature version indicated by 1st byte but not enough bytes for valid format")
}
claimID = raw_claim[1:21] // channel claimid = next 20 bytes
signature = raw_claim[21:85] // signature = next 64 bytes
pbPayload = raw_claim[85:] // protobuf payload = remaining bytes
}
claim_pb = &pb.Claim{}
err := proto.Unmarshal(pbPayload, claim_pb)
if err != nil {
legacy_claim_pb = &legacy_pb.Claim{}
legacyErr := proto.Unmarshal(raw_claim, legacy_claim_pb)
if legacyErr == nil {
claim_pb, err = migrateV1PBClaim(*legacy_claim_pb)
if err != nil {
return errors.Prefix(migrationErrorMessage, err)
}
if legacy_claim_pb.GetPublisherSignature() != nil {
version = WithSig
claimID = legacy_claim_pb.GetPublisherSignature().GetCertificateId()
signature = legacy_claim_pb.GetPublisherSignature().GetSignature()
}
if legacy_claim_pb.GetCertificate() != nil {
version = NoSig
}
} else {
return err
}
}
*c = ClaimHelper{
Claim: claim_pb,
LegacyClaim: legacy_claim_pb,
ClaimID: claimID,
Version: version,
Signature: signature,
Payload: pbPayload,
}
// Commenting out because of a bug in SDK release allowing empty addresses.
//err = c.ValidateAddresses(blockchainName)
//if err != nil {
// return err
//}
err = c.ValidateCertificate()
if err != nil {
return err
}
return nil
}
func (c *ClaimHelper) LoadFromHexString(claim_hex string, blockchainName string) error {
buf, err := hex.DecodeString(claim_hex)
if err != nil {
return err
}
return c.LoadFromBytes(buf, blockchainName)
}
func DecodeClaimProtoBytes(serialized []byte, blockchainName string) (*ClaimHelper, error) {
claim := &ClaimHelper{&pb.Claim{}, nil, nil, NoSig, nil, nil}
err := claim.LoadFromBytes(serialized, blockchainName)
if err != nil {
return nil, err
}
return claim, nil
}
func DecodeClaimHex(serialized string, blockchainName string) (*ClaimHelper, error) {
claim_bytes, err := hex.DecodeString(serialized)
if err != nil {
return nil, errors.Err(err)
}
return DecodeClaimBytes(claim_bytes, blockchainName)
}
// DecodeClaimBytes take a byte array and tries to decode it to a protobuf claim or migrate it from either json v1,2,3 or pb v1
func DecodeClaimBytes(serialized []byte, blockchainName string) (*ClaimHelper, error) {
helper, err := DecodeClaimProtoBytes(serialized, blockchainName)
if err == nil {
return helper, nil
}
helper = &ClaimHelper{}
//If protobuf fails, try json versions before returning an error.
v1Claim := new(V1Claim)
err = v1Claim.Unmarshal(serialized)
if err != nil {
v2Claim := new(V2Claim)
err := v2Claim.Unmarshal(serialized)
if err != nil {
v3Claim := new(V3Claim)
err := v3Claim.Unmarshal(serialized)
if err != nil {
return nil, errors.Prefix("Claim value has no matching version", err)
}
helper.Claim, err = migrateV3Claim(*v3Claim)
if err != nil {
return nil, errors.Prefix("V3 Metadata Migration Error", err)
}
return helper, nil
}
helper.Claim, err = migrateV2Claim(*v2Claim)
if err != nil {
return nil, errors.Prefix("V2 Metadata Migration Error ", err)
}
return helper, nil
}
helper.Claim, err = migrateV1Claim(*v1Claim)
if err != nil {
return nil, errors.Prefix("V1 Metadata Migration Error ", err)
}
return helper, nil
}
func (c *ClaimHelper) GetStream() *pb.Stream {
if c != nil {
return c.Claim.GetStream()
}
return nil
}
func (c *ClaimHelper) CompileValue() ([]byte, error) {
payload, err := c.serialized()
if err != nil {
return nil, err
}
var value []byte
value = append(value, c.Version.byte())
if c.Version == WithSig {
value = append(value, c.ClaimID...)
value = append(value, c.Signature...)
}
value = append(value, payload...)
return value, nil
}

View file

@ -0,0 +1,33 @@
package claim
import "testing"
func TestClaimHelper(t *testing.T) {
for _, rawClaim := range raw_claims {
helper, err := DecodeClaimHex(rawClaim, "lbrycrd_main")
if err != nil {
t.Error(err)
}
_, err = helper.RenderJSON()
if err != nil {
t.Error(err)
}
_, err = helper.serialized()
if err != nil {
t.Error(err)
}
_, err = helper.serializedHexString()
if err != nil {
t.Error(err)
}
_, err = helper.serializedNoSignature()
if err != nil {
t.Error(err)
}
err = helper.ValidateAddresses("lbrycrd_main")
if err != nil {
t.Error(err)
}
}
}

103
schema/claim/decode_test.go Normal file
View file

@ -0,0 +1,103 @@
package claim
import (
"encoding/hex"
"testing"
pb "github.com/lbryio/types/v2/go"
"github.com/btcsuite/btcd/btcec"
)
type rawClaim struct {
Hex string
ClaimID string
}
var raw_claims = []string{
"08011002225e0801100322583056301006072a8648ce3d020106052b8104000a03420004d015365a40f3e5c03c87227168e5851f44659837bcf6a3398ae633bc37d04ee19baeb26dc888003bd728146dbea39f5344bf8c52cedaf1a3a1623a0166f4a367",
"080110011ad7010801128f01080410011a0c47616d65206f66206c696665221047616d65206f66206c696665206769662a0b4a6f686e20436f6e776179322e437265617469766520436f6d6d6f6e73204174747269627574696f6e20342e3020496e7465726e6174696f6e616c38004224080110011a195569c917f18bf5d2d67f1346aa467b218ba90cdbf2795676da250000803f4a0052005a001a41080110011a30b6adf6e2a62950407ea9fb045a96127b67d39088678d2f738c359894c88d95698075ee6203533d3c204330713aa7acaf2209696d6167652f6769662a5c080110031a40c73fe1be4f1743c2996102eec6ce0509e03744ab940c97d19ddb3b25596206367ab1a3d2583b16c04d2717eeb983ae8f84fee2a46621ffa5c4726b30174c6ff82214251305ca93d4dbedb50dceb282ebcb7b07b7ac65",
"080110011ad7010801128f01080410011a0c47616d65206f66206c696665221047616d65206f66206c696665206769662a0b4a6f686e20436f6e776179322e437265617469766520436f6d6d6f6e73204174747269627574696f6e20342e3020496e7465726e6174696f6e616c38004224080110011a195569c917f18bf5d2d67f1346aa467b218ba90cdbf2795676da250000803f4a0052005a001a41080110011a30b6adf6e2a62950407ea9fb045a96127b67d39088678d2f738c359894c88d95698075ee6203533d3c204330713aa7acaf2209696d6167652f676966",
"080110011af901080112b101080410011a1c43414e47474948207c204b412044494c554152204e4547455249207c222c0a68747470733a2f2f7777772e796f75747562652e636f6d2f77617463683f763d5f5470313577746e7753732a1042616d62616e6720536574796177616e321c436f7079726967687465642028636f6e7461637420617574686f722938004a2968747470733a2f2f6265726b2e6e696e6a612f7468756d626e61696c732f5f5470313577746e77537352005a001a41080110011a304616bfdbb6fcb870d4c235443f9261d289ee8edbd4a51b8c6e3e95a34baeebbbb08978a7c5f9bf9a36245513b450943b2209766964656f2f6d70342a5c080110031a40f94a9db9c70217e4f17f9d38f08770096e7ce94a86b742b972e07c62c9606459c6ad735cd517175cf76ad6ea9eb16ca8198a17e2d31dc3ac53413005b5ca2a3a221402b1839207e2a706f0ba73dec0ce6b719043293d",
"080110011aa510080112dd0f080410011a4b4953545249204d4142554b204e41494b204b412021207b547269707d205369646f61726a6f202d2042616e797577616e67692042617275204e61696b204b41205372692054616e6a756e6722a80e4b657265746120617069205372692054616e6a756e67206164616c61682072616e676b6169616e206b657265746120617069206b656c617320656b6f6e6f6d69204143206a6172616b206a617568206d696c696b205054204b65726574612041706920496e646f6e6573696120285065727365726f292079616e67206d656c6179616e6920727574652042616e797577616e676920426172752d4c656d707579616e67616e2c2070702e204b65726574612061706920696e692064696f7065726173696b616e206f6c656820446165726168204f706572617369204958204a656d6265722c2079616e67206469616d62696c2064617269205372692054616e6a756e672c206e616d6120746f6b6f682064616c616d206365726974612072616b7961742042616e797577616e67692e0a0a4b65726574612061706920696e6920626572616e676b617420646172692042616e797577616e67692070756b756c2030362e3330205749422074696261206469204c656d707579616e67616e2070756b756c2031392e3330205749422c20736564616e676b616e20626572616e676b61742064617269204c656d707579616e67616e2070756b756c2030372e31352057494220746962612064692042616e797577616e67692070756b756c2032312e3135205749422e204b65726574612061706920696e69206d656d62617761207361747520676572626f6e6720616c696e672d616c696e6720626572757061206b65726574612070656d62616e676b6974202862696173616e7961204b5033292c20656e616d206b65726574612070656e756d70616e67206b656c617320656b6f6e6f6d692c2073617475206b6572657461206d616b616e2070656d62616e676b6974206b656c617320656b6f6e6f6d692c2064616e2068616d7069722073656c616c75206d656d626177612073617475206b65726574612062616761736920756e696b2079616e67206265727761726e61206269727520706f6c6f732e204e616d756e2073656972696e672064656e67616e2070656d62617275616e20696d616765205054204b41492c207365636172612062657274616861702073656d756120676572626f6e67206d656e6767756e616b616e206c697665727920224b65736570616b6174616e22206d656e67696b757469206b657265746120617069206c61696e6e79612e0a0a44616c616d207065726a616c616e616e6e79612c206b65726574612061706920696e692062657268656e7469206469205374617369756e2042616e797577616e676920426172752c204b6172616e676173656d2c20526f676f6a616d70692c2054656d7567757275682c204b616c6973657461696c2c2053756d626572776164756e672c20476c656e6d6f72652c204b616c69626172752c204b616c697361742c204a656d6265722c2052616d626970756a692c2054616e6767756c2c2050726f626f6c696e67676f2c20506173757275616e2c2042616e67696c2c205369646f61726a6f2c20537572616261796120477562656e672c20576f6e6f6b726f6d6f2c204d6f6a6f6b6572746f2c204a6f6d62616e672c204b6572746f736f6e6f2c204e67616e6a756b2c204361727562616e2c204d616469756e2c2042617261742c205061726f6e2c2057616c696b756b756e2c2053726167656e2c20507572776f736172692c204b6c6174656e2c2064616e204c656d707579616e67616e2c2064656e67616e20746f74616c2077616b74752074656d7075682073656b697461722031332d3134206a616d2e0a0a4b687573757320756e74756b206b652061726168205374617369756e2042616e797577616e6769204261727520284b41203139342f313935292c206b657265746120696e692064617061742062657268656e7469206469205374617369756e205361726164616e2028756e74756b2070657273696c616e67616e292c204261726f6e2c2053756d6f6269746f2028756e74756b2070657273696c616e67616e292c2064616e20536570616e6a616e672e204469205374617369756e20537572616261796120477562656e672c2064696c616b756b616e2070656d696e646168616e20706f73697369206c6f6b6f6d6f7469662e0a0a5061646120746168756e2032303136207461726966204b41205372692054616e6a756e67206b656d62616c69206469737562736964692070656d6572696e7461682e20506164612074616e6767616c2031204a616e756172692068696e676761203331204d617265742074617269666e7961206164616c6168205270203130302e3030302c30302c206d756c6169203120417072696c2074617269666e7961206164616c61682052702039362e3030302c30302c2064616e206d756c61692031204a756c692074617269666e7961206164616c61682052702039342e3030302c30302e202857696b697065646961290a68747470733a2f2f7777772e796f75747562652e636f6d2f77617463683f763d754750344b5857614536512a1042616d62616e6720536574796177616e321c436f7079726967687465642028636f6e7461637420617574686f722938004a2968747470733a2f2f6265726b2e6e696e6a612f7468756d626e61696c732f754750344b58576145365152005a001a41080110011a30d3d1d49ce3268e3dcf318ebbb6f4bfd454995d6b772bd5e27630743c0fb1d66387bf84b51afe28733812c5495b837b8f2209766964656f2f6d70342a5c080110031a40a47aa2d45ec15d1e578b91e5c8c76ee8a82e55af37da4873a7703795889ee7400967cf41e903788bcf0510d7c06976c99983fa01e702e1fb6d518b0646b0d565221402b1839207e2a706f0ba73dec0ce6b719043293d",
"080110011aa30d080112db0c080410011a3b5354415349554e2042414e595557414e47492042415255207c2050656e6767616e7469205374617369756e2042616e797577616e6769204c616d6122b60b5374617369756e2042616e797577616e676920426172752028425729206164616c6168207374617369756e206b657265746120617069206b656c61732062657361722079616e6720626572616461206469204b65746170616e672c204b616c697075726f2c2042616e797577616e67692e205374617369756e2079616e67207465726c6574616b2070616461206b6574696e676769616e202b37206d20696e69206d65727570616b616e207374617369756e2079616e67206c6574616b6e79612070616c696e672074696d757220646920446165726168204f706572617369204958204a656d6265722e205374617369756e20696e692062657261646120646920756a756e672070616c696e672074696d75722050756c6175204a6177612064616e2068616e7961206265726a6172616b20313030206d6574657220646172692050656c61627568616e2046657269204b65746170616e6720736568696e676761207374617369756e20696e69206a75676120736572696e672064697365627574205374617369756e204b65746170616e672e205374617369756e20696e69206a756761206d65727570616b616e207374617369756e206b65726574612061706920616b7469662079616e67206265726c6f6b6173692070616c696e672074696d75722064692042616e797577616e67692c204a6177612054696d75722c2064616e20496e646f6e657369612e205374617369756e20696e6920646962616e67756e2062657273616d61616e2064656e67616e2070656d62616e67756e616e206a616c757220626172752064617269207374617369756e206e6f6e20616b746966204b61626174206d656e756a752070656c61627568616e207465727365627574207061646120746168756e20313938353b20646966756e6773696b616e20756e74756b206d656e6767616e74696b616e205374617369756e2042616e797577616e6769204c616d612079616e67206164612064692077696c61796168206b6f74612042616e797577616e67692e205374617369756e2042616e797577616e67692042617275207465726c6574616b203130206b6d20646172692077696c61796168206b6f7461206b6520617261682075746172613b20646962616e67756e20756e74756b206d656d656e756869206b656275747568616e207472616e73706f72746173692079616e672073696e657267697320616e74617261206b6572657461206170692064656e67616e206b6170616c20666572692064692070656e7965626572616e67616e204b65746170616e672e205374617369756e20696e69206d656d696c696b6920656e616d206a616c75722064656e67616e206a616c757220322073656261676169207365707572206c757275732e0a5374617369756e20696e692064696c656e676b6170692064656e67616e20737562206469706f206c6f6b6f6d6f7469662064616e206469706f206b65726574612020756e74756b206d656e79696d70616e2c206d6572617761742061726d616461206b657265746120617069206b68757375736e7961206d696c696b2044616f70204958206974752073656e646972692c206a756761206d656d70756e79616920207475726e7461626c652042616c6c6f6f6e204c6f6f702079616e67207465726c6574616b20646920736562656c61682075746172612e0a5374617369756e20696e69206a756761206d656c6179616e6920616e676b7574616e20626172616e672c207961697475206b6572657461206170692053656d656e205469676120526f64612079616e67206469626572616e676b61746b616e2064617269205374617369756e204e616d626f2064616e206d656e6a616469204b412079616e67206d656e656d707568206a6172616b2070616c696e67206a61756820646920496e646f6e657369612e5b77696b6970656469615d0a68747470733a2f2f7777772e796f75747562652e636f6d2f77617463683f763d475473535a5a30794a53452a1042616d62616e6720536574796177616e321c436f7079726967687465642028636f6e7461637420617574686f722938004a2968747470733a2f2f6265726b2e6e696e6a612f7468756d626e61696c732f475473535a5a30794a534552005a001a41080110011a3048b3efe92661810e11118c9f8c0b4b4d1bca195eb6f74c8325070d97c699f4fb7ecc9ac90b3decc4feeb2ea0431e65922209766964656f2f6d70342a5c080110031a408e9fc836cad00c52ec7cdc95c11fc5369874948891df2187faaee212ca0925fc1058df5339c153dc00f055a8a21b853fb449a8ccb25ea52a98ba5645b22bdbfb221402b1839207e2a706f0ba73dec0ce6b719043293d",
"080110011aa40b080112dc0a080410011a465354415349554e2054454d504548202854504529207c504155442d4b422053454b4152204152554d7c204a616c7572204d617469204c756d616a616e672d506173697269616e22ac095374617369756e2054656d706568202854504529206b6574696e676769616e202b3933206d206d65727570616b616e207374617369756e206b657265746120617069206d61746920286e6f6e20616b746966292079616e67207465726c6574616b20646920447573756e2054756c757372656a6f20492054656d706568204c6f72204b6563616d6174616e2054656d7065682c204b616275706174656e204c756d616a616e672064656e67616e204b6f6f7264696e6174203a203038c2b03131e280b235372e32e280b34c532c313133c2b03130e280b232392e38e280b342542e205374617369756e20696e69206d65727570616b616e2073616c61682073617475207374617369756e2070616461206a616c7572206b657265746120617069204c756d616a616e672d506173697269616e2079616e67206d756c616920646967756e616b616e2074616e6767616c203136204d65692031383936202064616e2074656c616820646974757475702073656d656e6a616b203120466562727561726920313938382e2050616461206d6173612070656e6a616a6168616e2042656c616e64612c206a616c75722d6a616c757220696e69206265726164612064692062617761682070656e67656c6f6c61616e2053746161747373706f6f722d20656e205472616d776567656e20284f6f737465726c696a6e656e29202853532d4f4c292e0a5361617420696e69206b6f6e64697369205374617369756e2054656d7065682074696e6767616c2062616e67756e616e207574616d612c2079616e672064696a6164696b616e20736562616761692074656d706174206265726d61696e206b616e616b2d6b616e616b205041554420e28093204b422053454b4152204152554d2e0a4a616c7572206b65726574612061706920696e692070616461206d617361206c616c75206d65727570616b616e206a616c75722079616e672063756b757020736962756b2c2064656e67616e205374617369756e204c756d616a616e67202d79616e67207465726265736172206469206a616c757220696e69206d656c6179616e692068616d706972203330302e3030302070656e756d70616e6720706572746168756e2064616e20626172616e672068696e676761203233207269627520746f6e206c6562696820646920616e7461726120746168756e20313935302d31393533202e204a756d6c61682070656e756d70616e672079616e67206e61696b2064617269205374617369756e2054656d7065682068616d70697220736574656e6761682062616e79616b6e79612079616e67206e61696b2064617269205374617369756e204c756d616a616e672e0a53656d656e74617261206a6172696e67616e2072656c2062657365727461206b656c656e676b6170616e20776573656c2064616e2070657273696e79616c616e6e79612074656c61682068616269732074616b20626572736973612e2042656b6173206a616c75722072656c6e7961206b696e69206d656e6a616469206a616c616e206b6563696c202867616e672920616e746172206b616d70756e672e202857696b697065646961290a68747470733a2f2f7777772e796f75747562652e636f6d2f77617463683f763d43704e33485f6f67695f6f2a1042616d62616e6720536574796177616e321c436f7079726967687465642028636f6e7461637420617574686f722938004a2968747470733a2f2f6265726b2e6e696e6a612f7468756d626e61696c732f43704e33485f6f67695f6f52005a001a41080110011a30c11a6c72dc5cf5bb9b80bb58e760893984010a219702062234ef6eb9ec9572353a3c1b5b4da91a57057ee671b454f3c22209766964656f2f6d70342a5c080110031a40a8134e7e6e123c0b9ca568d95d8804da8a70877bdc47ca9b1c536db3a0e35a0de213ee66e3df77d42fb0c47cbd32c901b344b3e017f355169d7f85722a124dc9221402b1839207e2a706f0ba73dec0ce6b719043293d",
"080110011a8307080112bb06080410011a484b4120534552415955202032313520444154414e47204449205354415349554e2042414e4a415220204d454d424157412050554c414e47207c4a75727573616e205057542d505345228905536161742073656c657361692070656e656c75737572616e206a616c7572206e6f6e20616b7469662042616e6a61722c2050616e67616e646172616e2073616d7061692043696a756c616e67206d61756e79612074656d75616e206469207374617369756e2042616e6a617220756e74756b206d656e67756361706b616e2073616c616d207065727069736168616e2064656e67616e204f6d204d6179626920507261626f776f2073616e67206d617374657220626c7573756b616e206a616c7572206e6f6e20616b7469662c207465726e79617461204b412079616e6720616b616e206d656d626177612070756c616e672062616c696b206b65204a616b6172746120646174616e672064756c75616e206461726920507572776f6b6572746f2064616e20736179612073656e64697269206d617369682062657261646120646961746173206a656d626174616e2f4f7665727061732079616e672062657261646120646920736562656c6168207374617369756e2042616e6a61722061726168204a616b617274612c20616b6869726e79612073616c616d207065727069736168616e2068616e79612064656e67616e206d656d766964696f6b616e206b65726574612079616e67206d656d626177616e79612070756c616e672062616c696b206b65204a616b6172746120646172692061746173204a656d626174616e2f4f7665727061732e0a536179612073656e646972692062616c696b206b652053757261626179612064656e67616e204b4120506173756e64616e2c20476f6f64627965206d7920667269656e6420746f206d65657420616761696e2e0a68747470733a2f2f7777772e796f75747562652e636f6d2f77617463683f763d596c6f594d3353447430452a1042616d62616e6720536574796177616e321c436f7079726967687465642028636f6e7461637420617574686f722938004a2968747470733a2f2f6265726b2e6e696e6a612f7468756d626e61696c732f596c6f594d33534474304552005a001a41080110011a308c591efe76bd6d31b39c553996f925b3002b6fc150116f0e8d6bf7654e6674c5b3a59baef24c50fa908580a02dd90ded2209766964656f2f6d70342a5c080110031a40cbef89584d26bbf2695e039a10f2b34749843d827323c530e63b0472407fc7b184d174634d91f05efee9b90c1706e319bd9641226728524952e2b9004400684d221402b1839207e2a706f0ba73dec0ce6b719043293d",
"080110011aed03080112a503080410011a3b4b41205345524159552020323136204449204a504c203432362d41205354415349554e2042414e4a4152207c4a75727573616e205053452d505754228002496e696c6168204b4120536572617975203231362079616e67206d656d62617761206d617374657220626c7573756b616e206a616c7572206e6f6e20616b746966204f6d204d6179626920507261626f776f202068747470733a2f2f7777772e796f75747562652e636f6d2f6368616e6e656c2f55435076355953496f59716f38364a525f4871626b437251202e0a5361617420696e6920616b616e206d656e656c7573757269206a616c7572206e6f6e20616b7469662042616e6a61722d50616e67616e646172616e2d43696a756c616e672e0a68747470733a2f2f7777772e796f75747562652e636f6d2f77617463683f763d4a6b4347615473774c35632a1042616d62616e6720536574796177616e321c436f7079726967687465642028636f6e7461637420617574686f722938004a2968747470733a2f2f6265726b2e6e696e6a612f7468756d626e61696c732f4a6b4347615473774c356352005a001a41080110011a302ed97c79df5eccb145f8f8e1e866be1a392004a6794347c08c7e851c5f00b1504092a9f3c0674c78805a73a33c8b1bf32209766964656f2f6d70342a5c080110031a40cbcec20908e60b5f6198aecc192d2a9e4b069aa58d9238cb7154e37c4d04f268feefe92c2705c14009acf32e7e876df180cff3afdea6c989e75b4861150d1644221402b1839207e2a706f0ba73dec0ce6b719043293d",
}
func TestDecodeClaim(t *testing.T) {
claimHex := "000aa4010a8a010a30f1303989f58396694b0c5982c97f7e9d9435841d92aa13f4b80f671c27110c469babc4fbf4bd764155eaac089cfc49e8121454554d205045204d45524e45204c41472e6d703418cad0c8012209766964656f2f6d70343230c2c9389731e2a9568f66c78d703736a8c341015ada2e46f5dcc87aa6f08ab17c02df2121d9f6ef74055827a29dfc75801a044e6f6e6532040803180a5a0908b001109001188102421054554d205045204d45524e45204c41474a0944657369206c6f636b62020801"
claim, err := DecodeClaimHex(claimHex, "lbrycrd_main")
if err != nil {
t.Error(err, claim.ClaimID)
}
}
func TestDecodeClaims(t *testing.T) {
for _, claim_hex := range raw_claims {
claim, err := DecodeClaimHex(claim_hex, "lbrycrd_main")
if err != nil {
t.Error(err)
}
serializedHex, err := claim.serializedHexString()
if err != nil {
t.Error(err)
}
if serializedHex != claim_hex {
t.Error("failed to re-serialize")
}
}
}
func TestStripSignature(t *testing.T) {
claimHex := raw_claims[1]
claim, err := DecodeClaimHex(claimHex, "lbrycrd_main")
if err != nil {
t.Error(err)
}
noSig, err := claim.serializedNoSignature()
if err != nil {
t.Error(err)
}
if hex.EncodeToString(noSig) != raw_claims[2] {
t.Error("failed to remove signature")
}
}
func TestCreateChannelClaim(t *testing.T) {
private, err := btcec.NewPrivateKey(btcec.S256())
if err != nil {
t.Error(err)
}
pubKeyBytes, err := PublicKeyToDER(private.PubKey())
if err != nil {
t.Error(err)
}
claim := &ClaimHelper{Claim: newChannelClaim(), Version: NoSig}
claim.GetChannel().PublicKey = pubKeyBytes
claim.Title = "Test Channel Title"
claim.Description = "Test Channel Description"
claim.GetChannel().Cover = &pb.Source{Url: "http://testcoverurl.com"}
claim.Tags = []string{"TagA", "TagB", "TagC"}
claim.Languages = []*pb.Language{{Language: pb.Language_en}, {Language: pb.Language_es}}
claim.Thumbnail = &pb.Source{Url: "http://thumbnailurl.com"}
claim.GetChannel().WebsiteUrl = "http://homepageurl.com"
claim.Locations = []*pb.Location{{Country: pb.Location_AD}, {Country: pb.Location_US, State: "NJ", City: "some city"}}
rawClaim, err := claim.CompileValue()
if err != nil {
t.Error(err)
}
claim, err = DecodeClaimBytes(rawClaim, "lbrycrd_main")
if err != nil {
t.Error(err)
}
if bytes, err := claim.CompileValue(); err != nil || len(bytes) != len(rawClaim) {
t.Error("decoded claim does not match original")
}
}

50
schema/claim/keys.go Normal file
View file

@ -0,0 +1,50 @@
package claim
import (
"crypto/elliptic"
"crypto/x509/pkix"
"encoding/asn1"
"github.com/lbryio/lbry.go/extras/errors"
"github.com/btcsuite/btcd/btcec"
)
func PublicKeyToDER(publicKey *btcec.PublicKey) ([]byte, error) {
var publicKeyBytes []byte
var publicKeyAlgorithm pkix.AlgorithmIdentifier
var err error
pub := publicKey.ToECDSA()
publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
//ans1 encoding oid for ecdsa public key https://github.com/golang/go/blob/release-branch.go1.12/src/crypto/x509/x509.go#L457
publicKeyAlgorithm.Algorithm = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
//asn1 encoding oid for secp256k1 https://github.com/bitpay/bitpay-go/blob/v2.2.2/key_utils/key_utils.go#L30
paramBytes, err := asn1.Marshal(asn1.ObjectIdentifier{1, 3, 132, 0, 10})
if err != nil {
return nil, errors.Err(err)
}
publicKeyAlgorithm.Parameters.FullBytes = paramBytes
return asn1.Marshal(publicKeyInfo{
Algorithm: publicKeyAlgorithm,
PublicKey: asn1.BitString{
Bytes: publicKeyBytes,
BitLength: 8 * len(publicKeyBytes),
},
})
}
func (c *ClaimHelper) GetPublicKey() (*btcec.PublicKey, error) {
if c.GetChannel() == nil {
return nil, errors.Err("claim is not of type channel, so there is no public key to get")
}
return getPublicKeyFromBytes(c.GetChannel().PublicKey)
}
func getPublicKeyFromBytes(pubKeyBytes []byte) (*btcec.PublicKey, error) {
PKInfo := publicKeyInfo{}
asn1.Unmarshal(pubKeyBytes, &PKInfo)
pubkeyBytes1 := []byte(PKInfo.PublicKey.Bytes)
return btcec.ParsePubKey(pubkeyBytes1, btcec.S256())
}

36
schema/claim/keys_test.go Normal file
View file

@ -0,0 +1,36 @@
package claim
import (
"testing"
"gotest.tools/assert"
)
// The purpose of this test, is to make sure the function converts btcec.PublicKey to DER format the same way
// lbry SDK does as this is the bytes that are put into protobuf and the same bytes are used for verify signatures.
// Making sure these
func TestPublicKeyToDER(t *testing.T) {
cert_claim_hex := "08011002225e0801100322583056301006072a8648ce3d020106052b8104000a03420004d015365a40f3e5c03c87227168e5851f44659837bcf6a3398ae633bc37d04ee19baeb26dc888003bd728146dbea39f5344bf8c52cedaf1a3a1623a0166f4a367"
cert_claim, err := DecodeClaimHex(cert_claim_hex, "lbrycrd_main")
if err != nil {
t.Error(err)
}
p1, err := getPublicKeyFromBytes(cert_claim.Claim.GetChannel().PublicKey)
if err != nil {
t.Error(err)
}
pubkeyBytes2, err := PublicKeyToDER(p1)
if err != nil {
t.Error(err)
}
for i, b := range cert_claim.Claim.GetChannel().PublicKey {
assert.Assert(t, b == pubkeyBytes2[i], "DER format in bytes must match!")
}
p2, err := getPublicKeyFromBytes(pubkeyBytes2)
if err != nil {
t.Error(err)
}
assert.Assert(t, p1.IsEqual(p2), "The keys produced must be the same key!")
}

197
schema/claim/migration.go Normal file
View file

@ -0,0 +1,197 @@
package claim
import (
"encoding/hex"
"github.com/lbryio/lbry.go/extras/errors"
v1pb "github.com/lbryio/types/v1/go"
pb "github.com/lbryio/types/v2/go"
"github.com/btcsuite/btcutil/base58"
)
const lbrySDHash = "lbry_sd_hash"
func newStreamClaim() *pb.Claim {
claimStream := new(pb.Claim_Stream)
stream := new(pb.Stream)
pbClaim := new(pb.Claim)
pbClaim.Type = claimStream
claimStream.Stream = stream
return pbClaim
}
func newChannelClaim() *pb.Claim {
claimChannel := new(pb.Claim_Channel)
channel := new(pb.Channel)
pbClaim := new(pb.Claim)
pbClaim.Type = claimChannel
claimChannel.Channel = channel
return pbClaim
}
func setMetaData(claim *pb.Claim, author string, description string, language pb.Language_Language, license string,
licenseURL *string, title string, thumbnail *string, nsfw bool) {
claim.Title = title
claim.Description = description
claim.GetStream().Author = author
claim.Languages = []*pb.Language{{Language: language}}
if thumbnail != nil {
source := new(pb.Source)
source.Url = *thumbnail
claim.Thumbnail = source
}
if nsfw {
claim.Tags = []string{"mature"}
}
claim.GetStream().License = license
if licenseURL != nil {
claim.GetStream().LicenseUrl = *licenseURL
}
}
func migrateV1PBClaim(vClaim v1pb.Claim) (*pb.Claim, error) {
if *vClaim.ClaimType == v1pb.Claim_streamType {
return migrateV1PBStream(vClaim)
}
if *vClaim.ClaimType == v1pb.Claim_certificateType {
return migrateV1PBChannel(vClaim)
}
return nil, errors.Err("Could not migrate v1 protobuf claim due to unknown type '%s'.", vClaim.ClaimType.String())
}
func migrateV1PBStream(vClaim v1pb.Claim) (*pb.Claim, error) {
claim := newStreamClaim()
source := new(pb.Source)
source.MediaType = vClaim.GetStream().GetSource().GetContentType()
source.SdHash = vClaim.GetStream().GetSource().GetSource()
claim.GetStream().Source = source
md := vClaim.GetStream().GetMetadata()
if md.GetFee() != nil {
claim.GetStream().Fee = new(pb.Fee)
claim.GetStream().GetFee().Amount = uint64(*md.GetFee().Amount * 100000000)
claim.GetStream().GetFee().Address = md.GetFee().GetAddress()
claim.GetStream().GetFee().Currency = pb.Fee_Currency(pb.Fee_Currency_value[md.GetFee().GetCurrency().String()])
}
if vClaim.GetStream().GetMetadata().GetNsfw() {
claim.Tags = []string{"mature"}
}
thumbnailSource := new(pb.Source)
thumbnailSource.Url = md.GetThumbnail()
claim.Thumbnail = thumbnailSource
language := pb.Language_Language(pb.Language_Language_value[md.GetLanguage().String()])
claim.Languages = []*pb.Language{{Language: language}}
claim.GetStream().LicenseUrl = md.GetLicenseUrl()
claim.GetStream().License = md.GetLicense()
claim.Title = md.GetTitle()
claim.Description = md.GetDescription()
claim.GetStream().Author = md.GetAuthor()
return claim, nil
}
func migrateV1PBChannel(vClaim v1pb.Claim) (*pb.Claim, error) {
claim := newChannelClaim()
claim.GetChannel().PublicKey = vClaim.GetCertificate().PublicKey
return claim, nil
}
func migrateV1Claim(vClaim V1Claim) (*pb.Claim, error) {
pbClaim := newStreamClaim()
//Stream
// -->Universal
setFee(vClaim.Fee, pbClaim)
// -->MetaData
language := pb.Language_Language(pb.Language_Language_value[vClaim.Language])
setMetaData(pbClaim, vClaim.Author, vClaim.Description, language,
vClaim.License, nil, vClaim.Title, vClaim.Thumbnail, false)
// -->Source
source := new(pb.Source)
source.MediaType = vClaim.ContentType
src, err := hex.DecodeString(vClaim.Sources.LbrySDHash)
if err != nil {
return nil, errors.Err(err)
}
source.SdHash = src
pbClaim.GetStream().Source = source
return pbClaim, nil
}
func migrateV2Claim(vClaim V2Claim) (*pb.Claim, error) {
pbClaim := newStreamClaim()
//Stream
// -->Fee
setFee(vClaim.Fee, pbClaim)
// -->MetaData
language := pb.Language_Language(pb.Language_Language_value[vClaim.Language])
setMetaData(pbClaim, vClaim.Author, vClaim.Description, language,
vClaim.License, vClaim.LicenseURL, vClaim.Title, vClaim.Thumbnail, vClaim.NSFW)
// -->Source
source := new(pb.Source)
source.MediaType = vClaim.ContentType
src, err := hex.DecodeString(vClaim.Sources.LbrySDHash)
if err != nil {
return nil, errors.Err(err)
}
source.SdHash = src
pbClaim.GetStream().Source = source
return pbClaim, nil
}
func migrateV3Claim(vClaim V3Claim) (*pb.Claim, error) {
pbClaim := newStreamClaim()
//Stream
// -->Fee
setFee(vClaim.Fee, pbClaim)
// -->MetaData
language := pb.Language_Language(pb.Language_Language_value[vClaim.Language])
setMetaData(pbClaim, vClaim.Author, vClaim.Description, language,
vClaim.License, vClaim.LicenseURL, vClaim.Title, vClaim.Thumbnail, vClaim.NSFW)
// -->Source
source := new(pb.Source)
source.MediaType = vClaim.ContentType
src, err := hex.DecodeString(vClaim.Sources.LbrySDHash)
if err != nil {
return nil, errors.Err(err)
}
source.SdHash = src
pbClaim.GetStream().Source = source
return pbClaim, nil
}
func setFee(fee *Fee, pbClaim *pb.Claim) {
if fee != nil {
amount := float32(0.0)
currency := pb.Fee_LBC
address := ""
if fee.BTC != nil {
amount = fee.BTC.Amount
currency = pb.Fee_BTC
address = fee.BTC.Address
} else if fee.LBC != nil {
amount = fee.LBC.Amount
currency = pb.Fee_LBC
address = fee.LBC.Address
} else if fee.USD != nil {
amount = fee.USD.Amount
currency = pb.Fee_USD
address = fee.USD.Address
}
pbClaim.GetStream().Fee = new(pb.Fee)
//Fee Settings
pbClaim.GetStream().GetFee().Amount = uint64(amount * 100000000)
pbClaim.GetStream().GetFee().Currency = currency
pbClaim.GetStream().GetFee().Address = base58.Decode(address)
}
}

View file

@ -0,0 +1,282 @@
package claim
import (
"bytes"
"encoding/hex"
"fmt"
"testing"
"github.com/lbryio/lbryschema.go/address"
"github.com/btcsuite/btcutil/base58"
"gotest.tools/assert"
)
type valueTestPair struct {
ValueAsHex string
Claim claimResult
}
type claimResult struct {
Version string
Author string
Title string
Description string
License string
LicenseURL string
FeeAmount float32
FeeCurrency string
FeeAddress string
ContentType string
Language string
LbrySDHash string
Thumbnail string
NSFW bool
}
var badJsonVersionTests = []string{
"7b22666565223a20352c2022766572223a2022302e302e33222c20226c6963656e7365223a202247504c20332e30222c20226c616e6775616765223a2022656e222c2022617574686f72223a2022576f6e64657220576f6d616e2031393933222c20227469746c65223a2022576f6e64657220576f6d616e2031393933222c2022736f7572636573223a207b226c6272795f73645f68617368223a2022323338663832363932356462323235353863646335636430373436303637626666393964636263333834333832666661303236646463373230373261366362323461393332346461373961626136643232373437313164653035353063396534227d2c20226e736677223a20747275652c2022636f6e74656e745f74797065223a2022696d6167652f6a706567222c20227468756d626e61696c223a2022687474703a2f2f692e696d6775722e636f6d2f4568344a4658732e6a7067222c20226465736372697074696f6e223a2022466f7220746865206c6f766521227d",
"7b22666565223a2022302e303031222c2022766572223a2022302e302e33222c20226465736372697074696f6e223a20224120717569636b206c6f6f6b2061742074686520536f6e79204c44502033363030204c617365726469736320706c61796572222c20226c6963656e7365223a20224c42525920696e63222c20227469746c65223a2022536f6e79204c44502033363030204c617365726469736320506c61796572222c2022617574686f72223a20225061756c204b6176616e616768222c20226c616e6775616765223a2022656e222c2022736f7572636573223a207b226c6272795f73645f68617368223a2022393962383766363064643136643730316538613562666238353130633938343239623866633563656538623764663333643665666135386464313133313261333665616437303638643133636364636331383563376465613730643930393261227d2c2022636f6e74656e745f74797065223a2022766964656f2f6d7034222c20226e736677223a2066616c73657d",
"080110011ac3bd01080112c2b501080410011a0b57696e205465737420313122065465737420322a0444617665322e437265617469766520436f6d6d6f6e73204174747269627574696f6e20332e3020556e697465642053746174657338004224080110031a19556bc2a9c28bc2b057c3aec2901d5445c3b17cc384c3bd29c3b72b5f74101cc2aec2aac2a1250000c2b2424a0052005a3868747470733a2f2f6372656174697665636f6d6d6f6e732e6f72672f6c6963656e7365732f62792f332e302f75732f6c6567616c636f64651a41080110011a302a1ec2a42cc383223f7cc2b4c291353d0f687675c289c3a34cc3a73942c383c3abc3b7330dc3912d162ac388c3bec3a5c2aec293c2b1c38f68c3a2c2bfc2907a04c3b8c39dc2b1742209696d6167652f706e67",
"7b22666565223a2022302e303031222c2022766572223a2022302e302e33222c20226465736372697074696f6e223a20224120717569636b206c6f6f6b2061742074686520536f6e79204c44502033363030204c617365726469736320706c61796572222c20226c6963656e7365223a20224c42525920696e63222c20227469746c65223a2022536f6e79204c44502033363030204c617365726469736320506c61796572222c2022617574686f72223a20225061756c204b6176616e616768222c20226c616e6775616765223a2022656e222c2022736f7572636573223a207b226c6272795f73645f68617368223a2022393962383766363064643136643730316538613562666238353130633938343239623866633563656538623764663333643665666135386464313133313261333665616437303638643133636364636331383563376465613730643930393261227d2c2022636f6e74656e745f74797065223a2022766964656f2f6d7034222c20226e736677223a2066616c73657d",
"7b22666565223a2022302e303031222c2022766572223a2022302e302e33222c20226465736372697074696f6e223a20224120717569636b206c6f6f6b2061742074686520536f6e79204c44502033363030204c617365726469736320706c61796572222c20226c6963656e7365223a20224c42525920696e63222c2022617574686f72223a20225061756c204b6176616e616768222c20227469746c65223a2022536f6e79204c44502033363030204c617365726469736320506c61796572222c20226c616e6775616765223a2022656e222c2022736f7572636573223a207b226c6272795f73645f68617368223a2022626639663033333464623237393634303730663035636637356262376131626664663431363864356565376130633866373563663661623536653965633363643039633534636636383262353366316531353030363930616165353530663134227d2c2022636f6e74656e745f74797065223a2022766964656f2f6d7034222c20226e736677223a2066616c73657d",
"30383031313030313161356330383031313231343038303431303030316130303232303032613030333230303338303034613030353230303561303031613432303830313130303131613330333933333030616561386262623636613238613966633031316231666238373634636364386338323633656233343532623733303832363233656534356431363866646530343836346435303563376535396539353430636263643766623336323230613639366436313637363532663661373036353637",
}
var jsonVersionTests = []valueTestPair{
{"7b22666565223a207b224c4243223a207b22616d6f756e74223a20312e302c202261646472657373223a2022625077474139683775696a6f79357541767a565051773951794c6f595a6568484a6f227d7d2c20226465736372697074696f6e223a2022313030304d4220746573742066696c6520746f206d65617375726520646f776e6c6f6164207370656564206f6e204c627279207032702d6e6574776f726b2e222c20226c6963656e7365223a20224e6f6e65222c2022617574686f72223a2022726f6f74222c20226c616e6775616765223a2022456e676c697368222c20227469746c65223a2022313030304d4220737065656420746573742066696c65222c2022736f7572636573223a207b226c6272795f73645f68617368223a2022626439343033336431336634663339303837303837303163616635363562666130396366616466326633346661646634613733666238366232393564316232316137653634383035393934653435623566626336353066333062616334383734227d2c2022636f6e74656e742d74797065223a20226170706c69636174696f6e2f6f637465742d73747265616d222c20227468756d626e61696c223a20222f686f6d65726f626572742f6c6272792f73706565642e6a7067227d",
claimResult{"0.0.1",
"root",
"1000MB speed test file",
"1000MB test file to measure download speed on Lbry p2p-network.",
"None",
"",
1,
"LBC",
"bPwGA9h7uijoy5uAvzVPQw9QyLoYZehHJo",
"application/octet-stream",
"", //"English" is not supported for conversion.
"bd94033d13f4f3908708701caf565bfa09cfadf2f34fadf4a73fb86b295d1b21a7e64805994e45b5fbc650f30bac4874",
"/homerobert/lbry/speed.jpg",
false,
},
},
{"7b22666565223a207b224c4243223a207b22616d6f756e74223a2035302e302c202261646472657373223a2022624c5673336966507275795a6e70596d46665432544c416d68715a76676a70514461227d7d2c20226465736372697074696f6e223a2022466f757220636f75706c6573206d65657420666f722053756e646179206272756e6368206f6e6c7920746f20646973636f76657220746865792061726520737475636b20696e206120686f75736520746f6765746865722061732074686520776f726c64206d61792062652061626f757420746f20656e642e222c20226c6963656e7365223a20224f7363696c6c6f73636f7065204c61626f7261746f72696573222c2022617574686f72223a20225772697474656e20616e6420646972656374656420627920546f646420426572676572222c20226c616e6775616765223a2022656e222c20227469746c65223a2022497427732061204469736173746572222c2022736f7572636573223a207b226c6272795f73645f68617368223a2022646363316266323838393361353033376561623965346139636437613462666536663736616436633231393730656134636565653031323266353032656630373936353764346463613435366234626533323439383439633465313836386238227d2c2022636f6e74656e742d74797065223a2022766964656f2f717569636b74696d65222c20227468756d626e61696c223a2022687474703a2f2f69612e6d656469612d696d64622e636f6d2f696d616765732f4d2f4d5635424d5451774e6a597a4d5451304d6c35424d6c3542616e426e586b46745a5463774e44557a4f444d354e7740402e5f56315f5359313030305f4352302c302c3637332c313030305f414c5f2e6a7067227d",
claimResult{"0.0.1",
"Written and directed by Todd Berger",
"It's a Disaster",
"Four couples meet for Sunday brunch only to discover they are stuck in a house together as the world may be about to end.",
"Oscilloscope Laboratories",
"",
50,
"LBC",
"bLVs3ifPruyZnpYmFfT2TLAmhqZvgjpQDa",
"video/quicktime",
"language:en ",
"dcc1bf28893a5037eab9e4a9cd7a4bfe6f76ad6c21970ea4ceee0122f502ef079657d4dca456b4be3249849c4e1868b8",
"http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg",
false,
},
},
{"7b226465736372697074696f6e223a202241647669736f7220416c6578205461626172726f6b206769766573206869732074616b65206f6e204c4252592e222c20226c6963656e7365223a20224c4252592c20496e632e222c2022617574686f72223a202253616d75656c20427279616e222c20226c616e6775616765223a2022656e222c20227469746c65223a20224d65657420746865205465616d20457069736f64652031222c2022736f7572636573223a207b226c6272795f73645f68617368223a2022373939613164653933623865353536643766363638313033613666666334386163356664363830316464346438396361653637373363383064383163323833373130666434666432356564363864306462656565323638663832393134313435227d2c2022636f6e74656e742d74797065223a2022766964656f2f6d7034227d",
claimResult{"0.0.1",
"Samuel Bryan",
"Meet the Team Episode 1",
"Advisor Alex Tabarrok gives his take on LBRY.",
"LBRY, Inc.",
"",
0,
"UNKNOWN_CURRENCY",
"",
"video/mp4",
"language:en ",
"799a1de93b8e556d7f668103a6ffc48ac5fd6801dd4d89cae6773c80d81c283710fd4fd25ed68d0dbeee268f82914145",
"http://ia.media-imdb.com/images/M/MV5BMTQwNjYzMTQ0Ml5BMl5BanBnXkFtZTcwNDUzODM5Nw@@._V1_SY1000_CR0,0,673,1000_AL_.jpg",
false,
},
},
{"7b226c616e6775616765223a2022656e222c2022666565223a207b22555344223a207b22616d6f756e74223a20302e30312c202261646472657373223a2022624d486d5a4b5a6250713662504245514663384d5870694468463966374d56784d52227d7d2c2022736f7572636573223a207b226c6272795f73645f68617368223a2022326264386439646431613231386337663536373137653533666135313065666435613863303839656431663236373561306638643062356238626233633165643338336362396633616562396238393137383937363133303532393339373961227d2c20226465736372697074696f6e223a2022636c6f756473222c20226c6963656e7365223a2022637265617469766520636f6d6d6f6e73222c2022617574686f72223a202268747470733a2f2f7777772e76696465657a792e636f6d2f636c6f7564732f323637362d6461726b2d73746f726d2d636c6f7564732d726f79616c74792d667265652d68642d73746f636b2d766964656f222c20226e736677223a2066616c73652c20227469746c65223a2022636c6f756473222c2022636f6e74656e742d74797065223a2022766964656f2f6d7034222c2022766572223a2022302e302e32227d",
claimResult{"0.0.2",
"https://www.videezy.com/clouds/2676-dark-storm-clouds-royalty-free-hd-stock-video",
"clouds",
"clouds",
"creative commons",
"",
0.01,
"USD",
"bMHmZKZbPq6bPBEQFc8MXpiDhF9f7MVxMR",
"video/mp4",
"language:en ",
"2bd8d9dd1a218c7f56717e53fa510efd5a8c089ed1f2675a0f8d0b5b8bb3c1ed383cb9f3aeb9b891789761305293979a",
"",
false,
},
},
{"7b226c6963656e7365223a2022437265617469766520436f6d6d6f6e73204174747269627574696f6e20332e3020556e6974656420537461746573222c2022666565223a207b224c4243223a207b22616d6f756e74223a20312c202261646472657373223a20226256507157775966766a424859426f7575766b6e62514d58555a46764c644573354d227d7d2c2022766572223a2022302e302e32222c20226465736372697074696f6e223a20227a222c20226c616e6775616765223a2022656e222c2022617574686f72223a202279222c20227469746c65223a202278222c2022736f7572636573223a207b226c6272795f73645f68617368223a2022376332316565323337333234653561353061333432353632306665366363343030643363636363303535313938363763646131623963313061393737313934653331323030343134643837313436626666343730626162376637643735343738227d2c20226e736677223a2066616c73652c20226c6963656e73655f75726c223a202268747470733a2f2f6372656174697665636f6d6d6f6e732e6f72672f6c6963656e7365732f62792f332e302f75732f6c6567616c636f6465222c2022636f6e74656e742d74797065223a2022746578742f706c61696e227d",
claimResult{"0.0.2",
"y",
"x",
"z",
"Creative Commons Attribution 3.0 United States",
"https://creativecommons.org/licenses/by/3.0/us/legalcode",
1,
"LBC",
"bVPqWwYfvjBHYBouuvknbQMXUZFvLdEs5M",
"text/plain",
"language:en ",
"7c21ee237324e5a50a3425620fe6cc400d3cccc05519867cda1b9c10a977194e31200414d87146bff470bab7f7d75478",
"",
false,
},
},
{"7b22666565223a207b22555344223a207b22616d6f756e74223a20302e342c202261646472657373223a202262485365334b417674565352346d365331317a6475754639584874777363446a6f45227d7d2c2022766572223a2022302e302e33222c20226c6963656e7365223a2022437265617469766520436f6d6d6f6e73204174747269627574696f6e20332e3020556e6974656420537461746573222c20226c616e6775616765223a2022656e222c20227469746c65223a2022526561647920506c61796572204f6e652028417564696f626f6f6b2031206f66203229222c2022617574686f72223a202245726e65737420436c696e65222c2022736f7572636573223a207b226c6272795f73645f68617368223a2022333430653164646130653834313463323166616662623166323866326338623338343832316665306431653261373134336134383130353435653634653236613431303530343264346434376463393735346236313865636466653064313931227d2c20226e736677223a2066616c73652c2022636f6e74656e745f74797065223a2022617564696f2f6d706567222c20226c6963656e73655f75726c223a202268747470733a2f2f6372656174697665636f6d6d6f6e732e6f72672f6c6963656e7365732f62792f332e302f75732f6c6567616c636f6465222c20227468756d626e61696c223a2022687474703a2f2f692e696d6775722e636f6d2f6c794b45485a632e6a7067222c20226465736372697074696f6e223a2022496e20746865207965617220323034342c2074686520776f726c64206973206772697070656420627920616e20656e65726779206372697369732063617573696e67207769646573707265616420736f6369616c2070726f626c656d7320616e642065636f6e6f6d696320737461676e6174696f6e2e20546865207072696d6172792065736361706520666f72206d6f73742070656f706c6520697320746865204f415349532c2061207669727475616c20756e6976657273652c20616363657373656420776974682061207669736f7220616e642068617074696320676c6f7665732e2049742066756e6374696f6e7320626f746820617320616e204d4d4f52504720616e642061732061207669727475616c20736f63696574792c2077697468206974732063757272656e6379206265696e6720746865206d6f737420737461626c652063757272656e637920696e2074686520776f726c642e204974207761732063726561746564206279204a616d65732048616c6c696461792c2077686f73652077696c6c206c656674206120736572696573206f6620636c75657320746f776172647320616e20456173746572204567672077697468696e20746865204f41534953207468617420776f756c64206772616e742077686f6576657220666f756e6420697420626f74682068697320666f7274756e6520616e6420636f6e74726f6c206f6620746865204f4153495320697473656c662e205468697320686173206c656420746f20616e20696e74656e736520696e74657265737420696e20616c6c2061737065637473206f662038307320706f702063756c747572652c2077686963682048616c6c69646179206d61646520636c65617220776f756c6420626520657373656e7469616c20746f2066696e64696e6720686973206567672e227d",
claimResult{"0.0.3",
"Ernest Cline",
"Ready Player One (Audiobook 1 of 2)",
"In the year 2044, the world is gripped by an energy crisis causing widespread social problems and economic stagnation. The primary escape for most people is the OASIS, a virtual universe, accessed with a visor and haptic gloves. It functions both as an MMORPG and as a virtual society, with its currency being the most stable currency in the world. It was created by James Halliday, whose will left a series of clues towards an Easter Egg within the OASIS that would grant whoever found it both his fortune and control of the OASIS itself. This has led to an intense interest in all aspects of 80s pop culture, which Halliday made clear would be essential to finding his egg.",
"Creative Commons Attribution 3.0 United States",
"https://creativecommons.org/licenses/by/3.0/us/legalcode",
0.4,
"USD",
"bHSe3KAvtVSR4m6S11zduuF9XHtwscDjoE",
"audio/mpeg",
"language:en ",
"340e1dda0e8414c21fafbb1f28f2c8b384821fe0d1e2a7143a4810545e64e26a4105042d4d47dc9754b618ecdfe0d191",
"http://i.imgur.com/lyKEHZc.jpg",
false,
},
},
{"7b22666565223a207b224c4243223a207b22616d6f756e74223a20312c202261646472657373223a202262525478744355706a3654764a48675763527347634861467972524c6b6b69586747227d7d2c2022766572223a2022302e302e33222c20226c6963656e7365223a2022436f707972696768742032303136206272617a7a657273222c20226c616e6775616765223a2022656e222c20227469746c65223a2022686f7420706f726e222c2022617574686f72223a20226272617a7a657273222c2022736f7572636573223a207b226c6272795f73645f68617368223a2022666330646163356366633532363335343936336666313736396636653733396334653432613037393034323066666162396661336236343031653933616535613035313565666639363066386339633237323930376564613666636261323534227d2c20226e736677223a20747275652c2022636f6e74656e745f74797065223a2022766964656f2f6d7034222c20226465736372697074696f6e223a2022686f7420706f726e5c6e6272617a7a657273227d",
claimResult{"0.0.3",
"brazzers",
"hot porn",
"hot porn\nbrazzers",
"Copyright 2016 brazzers",
"",
1,
"LBC",
"bRTxtCUpj6TvJHgWcRsGcHaFyrRLkkiXgG",
"video/mp4",
"language:en ",
"fc0dac5cfc526354963ff1769f6e739c4e42a0790420ffab9fa3b6401e93ae5a0515eff960f8c9c272907eda6fcba254",
"",
true,
},
},
}
func TestBadMigrationFromJSON(t *testing.T) {
for _, hexStr := range badJsonVersionTests {
valueBytes, err := hex.DecodeString(hexStr)
if err != nil {
t.Error(err)
}
_, err = DecodeClaimBytes(valueBytes, "lbrycrd_main")
if err == nil {
t.Error(fmt.Sprintf("Decode error: The raw claim '%s' should have failed decoding.", hexStr))
}
}
}
func TestMigrationFromJSON(t *testing.T) {
for _, pair := range jsonVersionTests {
valueBytes, err := hex.DecodeString(pair.ValueAsHex)
if err != nil {
t.Error(err)
}
helper, err := DecodeClaimBytes(valueBytes, "lbrycrd_main")
if err != nil {
t.Error("Decode error: ", err)
}
if helper.Claim.GetStream().GetAuthor() != pair.Claim.Author {
t.Error("Author mismatch: expected", pair.Claim.Author, "got", helper.Claim.GetStream().GetAuthor())
}
if helper.Claim.GetTitle() != pair.Claim.Title {
t.Error("Title mismatch: expected", pair.Claim.Title, "got '", helper.Claim.GetTitle(), "'")
}
if helper.Claim.GetDescription() != pair.Claim.Description {
t.Error("Description mismatch: expected", pair.Claim.Description, "got '", helper.Claim.GetDescription(), "'")
}
if helper.Claim.GetStream().GetLicense() != pair.Claim.License {
t.Error("License mismatch: expected", pair.Claim.License, "got", helper.Claim.GetStream().GetLicense())
}
if helper.Claim.GetStream().GetLicenseUrl() != pair.Claim.LicenseURL {
t.Error("LicenseURL mismatch: expected", pair.Claim.LicenseURL, "got", helper.Claim.GetStream().GetLicenseUrl())
}
if helper.Claim.GetStream().GetFee().GetAmount() != uint64(pair.Claim.FeeAmount*100000000) {
t.Error("Fee Amount mismatch: expected", pair.Claim.FeeAmount, "got", helper.Claim.GetStream().GetFee().GetAmount())
}
if helper.Claim.GetStream().GetFee().GetCurrency().String() != pair.Claim.FeeCurrency {
t.Error("Fee Currency mismatch: expected", pair.Claim.FeeCurrency, "got", helper.Claim.GetStream().GetFee().GetCurrency())
}
hexaddress := base58.Encode(helper.Claim.GetStream().GetFee().GetAddress())
if hexaddress != pair.Claim.FeeAddress {
t.Error("Fee Address mismatch: expected", pair.Claim.FeeAddress, "got", hexaddress)
}
if helper.Claim.GetStream().GetSource().GetMediaType() != pair.Claim.ContentType {
t.Error("ContentType mismatch: expected", pair.Claim.ContentType, "got", helper.Claim.GetStream().GetSource().GetMediaType())
}
if helper.Claim.GetLanguages()[0].String() != pair.Claim.Language {
t.Error("Language mismatch: expected ", pair.Claim.Language, " got ", helper.Claim.GetLanguages()[0].String())
}
content := hex.EncodeToString(helper.Claim.GetStream().GetSource().GetSdHash())
if content != pair.Claim.LbrySDHash {
t.Error("Source mismatch: expected", pair.Claim.LbrySDHash, "got", content)
}
}
}
func TestMigrationFromV1YTSync(t *testing.T) {
claimHex := "080110011aee04080112a604080410011a2b4865726520617265203520526561736f6e73204920e29da4efb88f204e657874636c6f7564207c20544c4722920346696e64206f7574206d6f72652061626f7574204e657874636c6f75643a2068747470733a2f2f6e657874636c6f75642e636f6d2f0a0a596f752063616e2066696e64206d65206f6e20746865736520736f6369616c733a0a202a20466f72756d733a2068747470733a2f2f666f72756d2e6865617679656c656d656e742e696f2f0a202a20506f64636173743a2068747470733a2f2f6f6666746f706963616c2e6e65740a202a2050617472656f6e3a2068747470733a2f2f70617472656f6e2e636f6d2f7468656c696e757867616d65720a202a204d657263683a2068747470733a2f2f746565737072696e672e636f6d2f73746f7265732f6f6666696369616c2d6c696e75782d67616d65720a202a205477697463683a2068747470733a2f2f7477697463682e74762f786f6e64616b0a202a20547769747465723a2068747470733a2f2f747769747465722e636f6d2f7468656c696e757867616d65720a0a2e2e2e0a68747470733a2f2f7777772e796f75747562652e636f6d2f77617463683f763d4672546442434f535f66632a0f546865204c696e75782047616d6572321c436f7079726967687465642028636f6e7461637420617574686f722938004a2968747470733a2f2f6265726b2e6e696e6a612f7468756d626e61696c732f4672546442434f535f666352005a001a41080110011a30040e8ac6e89c061f982528c23ad33829fd7146435bf7a4cc22f0bff70c4fe0b91fd36da9a375e3e1c171db825bf5d1f32209766964656f2f6d70342a5c080110031a4062b2dd4c45e364030fbfad1a6fefff695ebf20ea33a5381b947753e2a0ca359989a5cc7d15e5392a0d354c0b68498382b2701b22c03beb8dcb91089031b871e72214feb61536c007cdf4faeeaab4876cb397feaf6b51"
claim, err := DecodeClaimHex(claimHex, "lbrycrd_main")
if err != nil {
t.Error(err)
}
assert.Assert(t, claim.GetTitle() == "Here are 5 Reasons I ❤️ Nextcloud | TLG")
assert.Assert(t, claim.GetDescription() == "Find out more about Nextcloud: https://nextcloud.com/\n\nYou can find me on these socials:\n * Forums: https://forum.heavyelement.io/\n * Podcast: https://offtopical.net\n * Patreon: https://patreon.com/thelinuxgamer\n * Merch: https://teespring.com/stores/official-linux-gamer\n * Twitch: https://twitch.tv/xondak\n * Twitter: https://twitter.com/thelinuxgamer\n\n...\nhttps://www.youtube.com/watch?v=FrTdBCOS_fc")
assert.Assert(t, claim.GetStream().GetLicense() == "Copyrighted (contact author)")
assert.Assert(t, claim.GetStream().GetAuthor() == "The Linux Gamer")
//?assert.Assert(t, claim.GetStream().GetLanguages()[0])
assert.Assert(t, claim.GetStream().GetSource().GetMediaType() == "video/mp4")
assert.Assert(t, claim.GetThumbnail().GetUrl() == "https://berk.ninja/thumbnails/FrTdBCOS_fc")
sdHashBytes, err := hex.DecodeString("040e8ac6e89c061f982528c23ad33829fd7146435bf7a4cc22f0bff70c4fe0b91fd36da9a375e3e1c171db825bf5d1f3")
if err != nil {
t.Error(err)
}
assert.Assert(t, bytes.Equal(claim.GetStream().GetSource().GetSdHash(), sdHashBytes))
channelHex := "08011002225e0801100322583056301006072a8648ce3d020106052b8104000a034200043878b1edd4a1373149909ef03f4339f6da9c2bd2214c040fd2e530463ffe66098eca14fc70b50ff3aefd106049a815f595ed5a13eda7419ad78d9ed7ae473f17"
channel, err := DecodeClaimHex(channelHex, "lbrycrd_main")
if err != nil {
t.Error(err)
}
pubKeyBytes, err := hex.DecodeString("3056301006072a8648ce3d020106052b8104000a034200043878b1edd4a1373149909ef03f4339f6da9c2bd2214c040fd2e530463ffe66098eca14fc70b50ff3aefd106049a815f595ed5a13eda7419ad78d9ed7ae473f17")
if err != nil {
t.Error(err)
}
assert.Assert(t, bytes.Equal(pubKeyBytes, channel.GetChannel().GetPublicKey()))
}
func TestMigrationFromV1UnsignedWithFee(t *testing.T) {
claimHex := "080110011ad6010801127c080410011a08727067206d69646922046d6964692a08727067206d696469322e437265617469766520436f6d6d6f6e73204174747269627574696f6e20342e3020496e7465726e6174696f6e616c38004224080110011a19553f00bc139bbf40de425f94d51fffb34c1bea6d9171cd374c25000070414a0052005a001a54080110011a301f41eb0312aa7e8a5ce49349bc77d811da975833719d751523b19f123fc3d528d6a94e3446ccddb7b9329f27a9cad7e3221c6170706c69636174696f6e2f782d7a69702d636f6d70726573736564"
claim, err := DecodeClaimHex(claimHex, "lbrycrd_main")
if err != nil {
t.Error(err)
}
assert.Assert(t, claim.GetTitle() == "rpg midi")
assert.Assert(t, claim.GetDescription() == "midi")
assert.Assert(t, claim.GetStream().GetLicense() == "Creative Commons Attribution 4.0 International")
assert.Assert(t, claim.GetStream().GetAuthor() == "rpg midi")
//assert.Assert(t, claim.GetStream().GetLanguage() == "en")
assert.Assert(t, claim.GetStream().GetSource().GetMediaType() == "application/x-zip-compressed")
sdHashBytes, err := hex.DecodeString("1f41eb0312aa7e8a5ce49349bc77d811da975833719d751523b19f123fc3d528d6a94e3446ccddb7b9329f27a9cad7e3")
if err != nil {
t.Error(err)
}
assert.Assert(t, bytes.Equal(claim.GetStream().GetSource().GetSdHash(), sdHashBytes))
feeAddressBytes, err := address.DecodeAddress("bJUQ9MxS9N6M29zsA5GTpVSDzsnPjMBBX9", "lbrycrd_main")
assert.Assert(t, bytes.Equal(claim.GetStream().GetFee().GetAddress(), feeAddressBytes[:]))
assert.Assert(t, claim.GetStream().GetFee().GetAmount() == 1500000000)
assert.Assert(t, claim.GetStream().GetFee().GetCurrency().String() == "LBC")
}

28
schema/claim/pretty.go Normal file
View file

@ -0,0 +1,28 @@
package claim
import (
"encoding/json"
"fmt"
"github.com/golang/protobuf/jsonpb"
)
func marshalToString(c *ClaimHelper) (string, error) {
m_pb := &jsonpb.Marshaler{}
return m_pb.MarshalToString(c)
}
func (c *ClaimHelper) RenderJSON() (string, error) {
r, err := marshalToString(c)
if err != nil {
fmt.Println("err")
return "", err
}
var dat map[string]interface{}
err = json.Unmarshal([]byte(r), &dat)
if err != nil {
return "", err
}
return r, nil
}
//TODO: encode byte arrays with b58 for addresses and b16 for source hashes instead of the default of b64

125
schema/claim/schema.go Normal file
View file

@ -0,0 +1,125 @@
package claim
import (
"encoding/json"
"github.com/lbryio/lbry.go/extras/errors"
)
// V1Claim is the first version of claim metadata used by lbry.
type V1Claim struct {
Version string `json:"ver,omitempty"`
Title string `json:"title"` //Required
Description string `json:"description"` //Required
Author string `json:"author"` //Required
Language string `json:"language"` //Required
License string `json:"license"` //Required
Sources Sources `json:"sources"` //Required
ContentType string `json:"content-type"` //Required
Thumbnail *string `json:"thumbnail,omitempty"`
Fee *Fee `json:"fee,omitempty"`
Contact *int `json:"contact,omitempty"`
PubKey *string `json:"pubkey,omitempty"`
}
// V2Claim is the second version of claim metadata used by lbry.
type V2Claim struct {
Version string `json:"ver"` //Required
Title string `json:"title"` //Required
Description string `json:"description"` //Required
Author string `json:"author"` //Required
Language string `json:"language"` //Required
License string `json:"license"` //Required
Sources Sources `json:"sources"` //Required
ContentType string `json:"content-type"` //Required
Thumbnail *string `json:"thumbnail,omitempty"`
Fee *Fee `json:"fee,omitempty"`
Contact *int `json:"contact,omitempty"`
PubKey *string `json:"pubkey,omitempty"`
LicenseURL *string `json:"license_url,omitempty"`
NSFW bool `json:"nsfw"` //Required
}
// V3Claim is the third version of claim metadata used by lbry.
type V3Claim struct {
Version string `json:"ver"` //Required
Title string `json:"title"` //Required
Description string `json:"description"` //Required
Author string `json:"author"` //Required
Language string `json:"language"` //Required
License string `json:"license"` //Required
Sources Sources `json:"sources"` //Required
ContentType string `json:"content_type"` //Required
Thumbnail *string `json:"thumbnail,omitempty"`
Fee *Fee `json:"fee,omitempty"`
Contact *int `json:"contact,omitempty"`
PubKey *string `json:"pubkey,omitempty"`
LicenseURL *string `json:"license_url,omitempty"`
NSFW bool `json:"nsfw"` //Required
Sig *string `json:"sig"`
}
// FeeInfo is the structure of fee information used by lbry.
type FeeInfo struct {
Amount float32 `json:"amount"` //Required
Address string `json:"address"` //Required
}
// Sources is the structure of Sources that can be used for a claim. Sources mainly include lbrysdhash but could be from
// elsewhere in the future.
type Sources struct {
LbrySDHash string `json:"lbry_sd_hash"` //Required
BTIH string `json:"btih"` //Required
URL string `json:"url"` //Required
}
// Fee is the structure used for different currencies allowed for claims.
type Fee struct {
LBC *FeeInfo `json:"LBC,omitempty"`
BTC *FeeInfo `json:"BTC,omitempty"`
USD *FeeInfo `json:"USD,omitempty"`
}
// Unmarshal is an implementation to unmarshal the V1 claim from json. Main addition is to check the version.
func (c *V1Claim) Unmarshal(value []byte) error {
err := json.Unmarshal(value, c)
if err != nil {
return err
} //Version can be blank for version 1
if c.Version != "" && c.Version != "0.0.1" {
err = errors.Base("Incorrect version, expected 0.0.1 found " + c.Version)
return err
}
//ToDo - restrict to required fields?
return nil
}
// Unmarshal is an implementation to unmarshal the V2 claim from json. Main addition is to check the version.
func (c *V2Claim) Unmarshal(value []byte) error {
err := json.Unmarshal(value, c)
if err != nil {
return err
}
if c.Version != "0.0.2" {
err = errors.Base("Incorrect version, expected 0.0.2 found " + c.Version)
return err
}
return nil
}
// Unmarshal is an implementation to unmarshal the V3 claim from json. Main addition is to check the version.
func (c *V3Claim) Unmarshal(value []byte) error {
err := json.Unmarshal(value, c)
if err != nil {
return err
}
if c.Version != "0.0.3" {
err = errors.Base("Incorrect version, expected 0.0.3 found " + c.Version)
return err
}
return nil
}

View file

@ -0,0 +1,90 @@
package claim
import (
"encoding/hex"
"github.com/lbryio/lbry.go/extras/errors"
legacy "github.com/lbryio/types/v1/go"
pb "github.com/lbryio/types/v2/go"
"github.com/golang/protobuf/proto"
)
func (c *ClaimHelper) serialized() ([]byte, error) {
serialized := c.String()
if serialized == "" {
return nil, errors.Err("not initialized")
}
if c.LegacyClaim != nil {
return proto.Marshal(c.getLegacyProtobuf())
}
return proto.Marshal(c.getProtobuf())
}
func (c *ClaimHelper) getProtobuf() *pb.Claim {
claim := &pb.Claim{
Title: c.GetTitle(),
Description: c.GetDescription(),
Thumbnail: c.GetThumbnail(),
Tags: c.GetTags(),
Languages: c.GetLanguages(),
Locations: c.GetLocations(),
}
if c.GetChannel() != nil {
claim.Type = &pb.Claim_Channel{Channel: c.GetChannel()}
} else if c.GetStream() != nil {
claim.Type = &pb.Claim_Stream{Stream: c.GetStream()}
} else if c.GetCollection() != nil {
claim.Type = &pb.Claim_Collection{Collection: c.GetCollection()}
} else if c.GetRepost() != nil {
claim.Type = &pb.Claim_Repost{Repost: c.GetRepost()}
}
return claim
}
func (c *ClaimHelper) getLegacyProtobuf() *legacy.Claim {
v := c.LegacyClaim.GetVersion()
t := c.LegacyClaim.GetClaimType()
return &legacy.Claim{
Version: &v,
ClaimType: &t,
Stream: c.LegacyClaim.GetStream(),
Certificate: c.LegacyClaim.GetCertificate(),
PublisherSignature: c.LegacyClaim.GetPublisherSignature()}
}
func (c *ClaimHelper) serializedHexString() (string, error) {
serialized, err := c.serialized()
if err != nil {
return "", err
}
serialized_hex := hex.EncodeToString(serialized)
return serialized_hex, nil
}
func (c *ClaimHelper) serializedNoSignature() ([]byte, error) {
if c.String() == "" {
return nil, errors.Err("not initialized")
}
if c.Signature == nil {
serialized, err := c.serialized()
if err != nil {
return nil, err
}
return serialized, nil
} else {
if c.LegacyClaim != nil {
clone := &legacy.Claim{}
proto.Merge(clone, c.getLegacyProtobuf())
proto.ClearAllExtensions(clone.PublisherSignature)
clone.PublisherSignature = nil
return proto.Marshal(clone)
}
clone := &pb.Claim{}
proto.Merge(clone, c.getProtobuf())
return proto.Marshal(clone)
}
}

112
schema/claim/sign.go Normal file
View file

@ -0,0 +1,112 @@
package claim
import (
"crypto/sha256"
"encoding/hex"
"github.com/lbryio/lbry.go/extras/errors"
"github.com/lbryio/lbryschema.go/address"
"github.com/btcsuite/btcd/btcec"
)
func Sign(privKey btcec.PrivateKey, channel ClaimHelper, claim ClaimHelper, k string) (*Signature, error) {
if channel.GetChannel() == nil {
return nil, errors.Err("claim as channel is not of type channel")
}
if claim.LegacyClaim != nil {
return claim.signV1(privKey, channel, k)
}
return claim.sign(privKey, channel, k)
}
func (c *ClaimHelper) sign(privKey btcec.PrivateKey, channel ClaimHelper, firstInputTxID string) (*Signature, error) {
txidBytes, err := hex.DecodeString(firstInputTxID)
if err != nil {
return nil, errors.Err(err)
}
metadataBytes, err := c.serialized()
if err != nil {
return nil, errors.Err(err)
}
var digest []byte
digest = append(digest, txidBytes...)
digest = append(digest, c.ClaimID...)
digest = append(digest, metadataBytes...)
hash := sha256.Sum256(digest)
hashBytes := make([]byte, len(hash))
for i, b := range hash {
hashBytes[i] = b
}
sig, err := privKey.Sign(hashBytes)
if err != nil {
return nil, errors.Err(err)
}
return &Signature{*sig}, nil
}
func (c *ClaimHelper) signV1(privKey btcec.PrivateKey, channel ClaimHelper, claimAddress string) (*Signature, error) {
metadataBytes, err := c.serializedNoSignature()
if err != nil {
return nil, errors.Err(err)
}
addressBytes, err := address.DecodeAddress(claimAddress, "lbrycrd_main")
if err != nil {
return nil, errors.Prefix("V1 signing requires claim address and the decode failed with: ", err)
}
var digest []byte
address := make([]byte, len(addressBytes))
for i, b := range addressBytes {
address[i] = b
}
digest = append(digest, address...)
digest = append(digest, metadataBytes...)
digest = append(digest, channel.ClaimID...)
hash := sha256.Sum256(digest)
hashBytes := make([]byte, len(hash))
for i, b := range hash {
hashBytes[i] = b
}
sig, err := privKey.Sign(hashBytes)
if err != nil {
return nil, errors.Err(err)
}
return &Signature{*sig}, nil
}
type Signature struct {
btcec.Signature
}
func (s *Signature) LBRYSDKEncode() ([]byte, error) {
if s.R == nil || s.S == nil {
return nil, errors.Err("invalid signature, both S & R are nil")
}
rBytes := s.R.Bytes()
sBytes := s.S.Bytes()
return append(rBytes, sBytes...), nil
}
// rev reverses a byte slice. useful for switching endian-ness
func reverseBytes(b []byte) []byte {
r := make([]byte, len(b))
for left, right := 0, len(b)-1; left < right; left, right = left+1, right-1 {
r[left], r[right] = b[right], b[left]
}
return r
}

137
schema/claim/sign_test.go Normal file
View file

@ -0,0 +1,137 @@
package claim
import (
"encoding/hex"
"testing"
"github.com/btcsuite/btcd/btcec"
"gotest.tools/assert"
)
func TestSign(t *testing.T) {
privateKey, err := btcec.NewPrivateKey(btcec.S256())
if err != nil {
t.Error(err)
return
}
channel := &ClaimHelper{newChannelClaim(), nil, nil, NoSig, nil, nil}
pubkeyBytes, err := PublicKeyToDER(privateKey.PubKey())
if err != nil {
t.Error(err)
return
}
channel.GetChannel().PublicKey = pubkeyBytes
claimID := "cf3f7c898af87cc69b06a6ac7899efb9a4878fdb" //Fake
txid := "4c1df9e022e396859175f9bfa69b38e444db10fb53355fa99a0989a83bcdb82f" //Fake
claimIDHexBytes, err := hex.DecodeString(claimID)
if err != nil {
t.Error(err)
return
}
claim := &ClaimHelper{newStreamClaim(), nil, reverseBytes(claimIDHexBytes), WithSig, nil, nil}
claim.Claim.Title = "Test title"
claim.Claim.Description = "Test description"
sig, err := Sign(*privateKey, *channel, *claim, txid)
if err != nil {
t.Error(err)
return
}
signatureBytes, err := sig.LBRYSDKEncode()
if err != nil {
t.Error(err)
return
}
claim.Signature = signatureBytes
rawChannel, err := channel.CompileValue()
if err != nil {
t.Error(err)
return
}
rawClaim, err := claim.CompileValue()
if err != nil {
t.Error(err)
return
}
channel, err = DecodeClaimBytes(rawChannel, "lbrycrd_main")
if err != nil {
t.Error(err)
return
}
claim, err = DecodeClaimBytes(rawClaim, "lbrycrd_main")
if err != nil {
t.Error(err)
return
}
valid, err := claim.ValidateClaimSignature(channel, txid, claimID, "lbrycrd_main")
if err != nil {
t.Error(err)
return
}
assert.Assert(t, valid, "could not verify signature")
}
func TestSignWithV1Channel(t *testing.T) {
cert_claim_hex := "08011002225e0801100322583056301006072a8648ce3d020106052b8104000a03420004d015365a40f3e5c03c87227168e5851f44659837bcf6a3398ae633bc37d04ee19baeb26dc888003bd728146dbea39f5344bf8c52cedaf1a3a1623a0166f4a367"
channel, err := DecodeClaimHex(cert_claim_hex, "lbrycrd_main")
if err != nil {
t.Error(err)
}
privateKey, err := btcec.NewPrivateKey(btcec.S256())
if err != nil {
t.Error(err)
return
}
pubkeyBytes, err := PublicKeyToDER(privateKey.PubKey())
if err != nil {
t.Error(err)
return
}
channel.GetChannel().PublicKey = pubkeyBytes
claimID := "251305ca93d4dbedb50dceb282ebcb7b07b7ac64"
txid := "4c1df9e022e396859175f9bfa69b38e444db10fb53355fa99a0989a83bcdb82f" //Fake
claimIDHexBytes, err := hex.DecodeString(claimID)
if err != nil {
t.Error(err)
return
}
claim := &ClaimHelper{newStreamClaim(), nil, reverseBytes(claimIDHexBytes), WithSig, nil, nil}
claim.Claim.Title = "Test title"
claim.Claim.Description = "Test description"
sig, err := Sign(*privateKey, *channel, *claim, txid)
if err != nil {
t.Error(err)
return
}
signatureBytes, err := sig.LBRYSDKEncode()
if err != nil {
t.Error(err)
return
}
claim.Signature = signatureBytes
compiledClaim, err := claim.CompileValue()
if err != nil {
t.Error(err)
}
claim, err = DecodeClaimBytes(compiledClaim, "lbrycrd_main")
valid, err := claim.ValidateClaimSignature(channel, txid, claimID, "lbrycrd_main")
if err != nil {
t.Error(err)
return
}
assert.Assert(t, valid, "could not verify signature")
}

128
schema/claim/validator.go Normal file
View file

@ -0,0 +1,128 @@
package claim
import (
"crypto/ecdsa"
"crypto/sha256"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/binary"
"encoding/hex"
"math/big"
"github.com/lbryio/lbry.go/extras/errors"
"github.com/lbryio/lbryschema.go/address"
)
type publicKeyInfo struct {
Raw asn1.RawContent
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
const SECP256k1 = "SECP256k1"
//const NIST256p = "NIST256p"
//const NIST384p = "NIST384p"
func getClaimSignatureDigest(bytes ...[]byte) [32]byte {
var combined []byte
for _, b := range bytes {
combined = append(combined, b...)
}
digest := sha256.Sum256(combined)
return [32]byte(digest)
}
func (c *ClaimHelper) VerifyDigest(certificate *ClaimHelper, signature [64]byte, digest [32]byte) bool {
if certificate == nil {
return false
}
R := &big.Int{}
S := &big.Int{}
R.SetBytes(signature[0:32])
S.SetBytes(signature[32:64])
pk, err := certificate.GetPublicKey()
if err != nil {
return false
}
return ecdsa.Verify(pk.ToECDSA(), digest[:], R, S)
}
func (c *ClaimHelper) ValidateClaimSignature(certificate *ClaimHelper, k string, certificateId string, blockchainName string) (bool, error) {
if c.LegacyClaim != nil {
return c.validateV1ClaimSignature(certificate, k, certificateId, blockchainName)
}
return c.validateClaimSignature(certificate, k, certificateId, blockchainName)
}
func (c *ClaimHelper) validateClaimSignature(certificate *ClaimHelper, firstInputTxHash, certificateId string, blockchainName string) (bool, error) {
certificateIdSlice, err := hex.DecodeString(certificateId)
if err != nil {
return false, errors.Err(err)
}
certificateIdSlice = reverseBytes(certificateIdSlice)
firstInputTxIDBytes, err := hex.DecodeString(firstInputTxHash)
if err != nil {
return false, errors.Err(err)
}
signature := c.Signature
if signature == nil {
return false, errors.Err("claim does not have a signature")
}
signatureBytes := [64]byte{}
for i, b := range signature {
signatureBytes[i] = b
}
claimDigest := getClaimSignatureDigest(firstInputTxIDBytes, certificateIdSlice, c.Payload)
return c.VerifyDigest(certificate, signatureBytes, claimDigest), nil
}
func (c *ClaimHelper) validateV1ClaimSignature(certificate *ClaimHelper, claimAddy string, certificateId string, blockchainName string) (bool, error) {
addressBytes, err := address.DecodeAddress(claimAddy, blockchainName)
if err != nil {
return false, err
}
//For V1 claim_id was incorrectly stored for claim signing.
// So the bytes are not reversed like they are supposed to be (Endianess)
certificateIdSlice, err := hex.DecodeString(certificateId)
if err != nil {
return false, err
}
signature := c.Signature
if signature == nil {
return false, errors.Err("claim does not have a signature")
}
signatureBytes := [64]byte{}
for i := range signatureBytes {
signatureBytes[i] = signature[i]
}
claimAddress, err := address.ValidateAddress(addressBytes, blockchainName)
if err != nil {
return false, errors.Err("invalid address")
}
serializedNoSig, err := c.serializedNoSignature()
if err != nil {
return false, errors.Err("serialization error")
}
claimDigest := getClaimSignatureDigest(claimAddress[:], serializedNoSig, certificateIdSlice)
return c.VerifyDigest(certificate, signatureBytes, claimDigest), nil
}
func GetOutpointHash(txid string, vout uint32) (string, error) {
txidBytes, err := hex.DecodeString(txid)
if err != nil {
return "", errors.Err(err)
}
var voutBytes = make([]byte, 4)
binary.LittleEndian.PutUint32(voutBytes, vout)
return hex.EncodeToString(append(reverseBytes(txidBytes), voutBytes...)), nil
}

View file

@ -0,0 +1,94 @@
package claim
import (
"testing"
"gotest.tools/assert"
)
func TestV1ValidateClaimSignature(t *testing.T) {
cert_claim_hex := "08011002225e0801100322583056301006072a8648ce3d020106052b8104000a03420004d015365a40f3e5c03c87227168e5851f44659837bcf6a3398ae633bc37d04ee19baeb26dc888003bd728146dbea39f5344bf8c52cedaf1a3a1623a0166f4a367"
signed_claim_hex := "080110011ad7010801128f01080410011a0c47616d65206f66206c696665221047616d65206f66206c696665206769662a0b4a6f686e20436f6e776179322e437265617469766520436f6d6d6f6e73204174747269627574696f6e20342e3020496e7465726e6174696f6e616c38004224080110011a195569c917f18bf5d2d67f1346aa467b218ba90cdbf2795676da250000803f4a0052005a001a41080110011a30b6adf6e2a62950407ea9fb045a96127b67d39088678d2f738c359894c88d95698075ee6203533d3c204330713aa7acaf2209696d6167652f6769662a5c080110031a40c73fe1be4f1743c2996102eec6ce0509e03744ab940c97d19ddb3b25596206367ab1a3d2583b16c04d2717eeb983ae8f84fee2a46621ffa5c4726b30174c6ff82214251305ca93d4dbedb50dceb282ebcb7b07b7ac65"
signed_claim, err := DecodeClaimHex(signed_claim_hex, "lbrycrd_main")
if err != nil {
t.Error(err)
}
cert_claim, err := DecodeClaimHex(cert_claim_hex, "lbrycrd_main")
if err != nil {
t.Error(err)
}
claim_addr := "bSkUov7HMWpYBiXackDwRnR5ishhGHvtJt"
cert_id := "251305ca93d4dbedb50dceb282ebcb7b07b7ac65"
result, err := signed_claim.ValidateClaimSignature(cert_claim, claim_addr, cert_id, "lbrycrd_main")
if err != nil {
t.Error(err)
}
if result != true {
t.Error("failed to validate signature:", result)
}
}
func TestV1FailToValidateClaimSignature(t *testing.T) {
cert_claim_hex := "08011002225e0801100322583056301006072a8648ce3d020106052b8104000a03420004d015365a40f3e5c03c87227168e5851f44659837bcf6a3398ae633bc37d04ee19baeb26dc888003bd728146dbea39f5344bf8c52cedaf1a3a1623a0166f4a367"
signed_claim_hex := "080110011ad7010801128f01080410011a0c47616d65206f66206c696665221047616d65206f66206c696665206769662a0b4a6f686e20436f6e776179322e437265617469766520436f6d6d6f6e73204174747269627574696f6e20342e3020496e7465726e6174696f6e616c38004224080110011a195569c917f18bf5d2d67f1346aa467b218ba90cdbf2795676da250000803f4a0052005a001a41080110011a30b6adf6e2a62950407ea9fb045a96127b67d39088678d2f738c359894c88d95698075ee6203533d3c204330713aa7acaf2209696d6167652f6769662a5c080110031a40c73fe1be4f1743c2996102eec6ce0509e03744ab940c97d19ddb3b25596206367ab1a3d2583b16c04d2717eeb983ae8f84fee2a46621ffa5c4726b30174c6ff82214251305ca93d4dbedb50dceb282ebcb7b07b7ac65"
signed_claim, err := DecodeClaimHex(signed_claim_hex, "lbrycrd_main")
if err != nil {
t.Error(err)
}
cert_claim, err := DecodeClaimHex(cert_claim_hex, "lbrycrd_main")
if err != nil {
t.Error(err)
}
claim_addr := "bSkUov7HMWpYBiXackDwRnR5ishhGHvtJt"
cert_id := "251305ca93d4dbedb50dceb282ebcb7b07b7ac64"
result, err := signed_claim.ValidateClaimSignature(cert_claim, claim_addr, cert_id, "lbrycrd_main")
if err != nil {
t.Error(err)
}
if result != false {
t.Error("failed to validate signature:", result)
}
}
func TestV2ValidateClaimSignature(t *testing.T) {
cert_claim_hex := "00125a0a583056301006072a8648ce3d020106052b8104000a034200045a0343c155302280da01ae0001b7295241eb03c42a837acf92ccb9680892f7db50fd1d3c14b28bb594e304f05fc4ae7c1f222a85d1d1a3461b3cfb9906f66cb5"
signed_claim_hex := "015cb78e424a34fbf79b67f9107430427aa62373e69b4998a29ecec8f14a9e0a213a043ced8064c069d7e464b5fd3ccb92b45bd59b15c0e1bb27e3c366d43f86a9a6b5ad42647a1aad69a73ac50b19ae3ec978c2c70aa2010a99010a301c662f19abc461e7eddecf165adfa7fca569e209773f3db31241c1e297f0a8d5b3e4768828b065fbeb1d6776f61073f6121b3031202d20556e6d6173746572656420496d70756c7365732e377a187a22146170706c69636174696f6e2f782d6578742d377a32302eb61ea475017e28c013616a56c1219ba90dc35fffff453d9675146f648f66634e0d1516528d37aba9f5801229d9f2181a044e6f6e6542087465737420707562520062020801"
signed_claim, err := DecodeClaimHex(signed_claim_hex, "lbrycrd_main")
if err != nil {
t.Error(err)
}
cert_claim, err := DecodeClaimHex(cert_claim_hex, "lbrycrd_main")
if err != nil {
t.Error(err)
}
firstInputTxHash, err := GetOutpointHash("becb96a4a2e66bd24f083772fe9da904654ea9b5f07cc5bfbee233355911ddb1", uint32(0))
if err != nil {
t.Error(err)
}
cert_id := "e67323a67a42307410f9679bf7fb344a428eb75c"
result, err := signed_claim.ValidateClaimSignature(cert_claim, firstInputTxHash, cert_id, "lbrycrd_main")
if err != nil {
t.Error(err)
}
if result != true {
t.Error("failed to validate signature:", result)
}
}
func TestGetOutpointHash(t *testing.T) {
hash, err := GetOutpointHash("dc3dcf2f94d3c91e454ac2474802e20f26b30705372dda43890c811d918aef64", 1)
if err != nil {
t.Error(err)
}
assert.Assert(t, hash == "64ef8a911d810c8943da2d370507b3260fe2024847c24a451ec9d3942fcf3ddc01000000", uint(1))
}

View file

@ -0,0 +1,43 @@
package main
import (
"fmt"
"github.com/lbryio/lbryschema.go/claim"
"os"
)
func main() {
args := os.Args[1:]
if len(args) == 1 {
claimBytes := []byte(args[0])
decoded, err := claim.DecodeClaimBytes(claimBytes, "lbrycrd_main")
if err != nil {
fmt.Println("Decoding error:", err)
return
}
text, err := decoded.RenderJSON()
if err != nil {
fmt.Println("Decoding error:", err)
return
}
fmt.Println(text)
return
} else if (len(args) == 2) && (args[1] == "--decode_hex") {
claimHex := args[0]
decoded, err := claim.DecodeClaimHex(claimHex, "lbrycrd_main")
if err != nil {
fmt.Println("Decoding error:", err)
return
}
text, err := decoded.RenderJSON()
if err != nil {
fmt.Println("Decoding error:", err)
return
}
fmt.Println(text)
return
} else {
fmt.Println("encountered an error\nusage: \n\tlbryschema-cli <value to decode> [--decode_hex]")
return
}
}

1
schema/test.sh Executable file
View file

@ -0,0 +1 @@
go test ./... -v