diff --git a/extras/jsonrpc/daemon.go b/extras/jsonrpc/daemon.go index 7e8ed52..ae2a535 100644 --- a/extras/jsonrpc/daemon.go +++ b/extras/jsonrpc/daemon.go @@ -1,8 +1,10 @@ package jsonrpc import ( + "encoding/hex" "encoding/json" "fmt" + "io/ioutil" "net/http" "reflect" "sort" @@ -11,6 +13,7 @@ import ( "time" "github.com/lbryio/lbry.go/extras/errors" + "github.com/lbryio/lbry.go/stream" "github.com/fatih/structs" "github.com/mitchellh/mapstructure" @@ -190,16 +193,16 @@ func (d *Client) AccountFund(fromAccount string, toAccount string, amount string }) } -func (d *Client) AccountCreate(accountName string, singleKey bool) (*AccountCreateResponse, error) { - response := new(AccountCreateResponse) +func (d *Client) AccountCreate(accountName string, singleKey bool) (*Account, error) { + response := new(Account) return response, d.call(response, "account_create", map[string]interface{}{ "account_name": accountName, "single_key": singleKey, }) } -func (d *Client) AccountRemove(accountID string) (*AccountRemoveResponse, error) { - response := new(AccountRemoveResponse) +func (d *Client) AccountRemove(accountID string) (*Account, error) { + response := new(Account) return response, d.call(response, "account_remove", map[string]interface{}{ "account_id": accountID, }) @@ -574,3 +577,80 @@ func (d *Client) SupportAbandon(claimID *string, txid *string, nout *uint, keep structs.DefaultTagName = "json" return response, d.call(response, "support_abandon", structs.Map(args)) } + +func (d *Client) AccountAdd(accountName string, seed *string, privateKey *string, publicKey *string, singleKey *bool, walletID *string) (*Account, error) { + response := new(Account) + + args := struct { + AccountName string `json:"account_name"` + Seed *string `json:"seed,omitempty"` + PrivateKey *string `json:"private_key,omitempty"` + PublicKey *string `json:"public_key,omitempty"` + SingleKey *bool `json:"single_key,omitempty"` + WalletID *string `json:"wallet_id,omitempty"` + }{ + AccountName: accountName, + Seed: seed, + PrivateKey: privateKey, + PublicKey: publicKey, + SingleKey: singleKey, + WalletID: walletID, + } + structs.DefaultTagName = "json" + return response, d.call(response, "account_add", structs.Map(args)) +} + +// GetStreamSizeByMagic uses "magic" to not just estimate, but actually return the exact size of a stream +// It does so by fetching the sd blob and the last blob from our S3 bucket, decrypting and unpadding the last blob +// adding up all full blobs that have a known size and finally adding the real last blob size too. +// This will only work if we host at least the sd blob and the last blob on S3, if not, this will error. +func (c *Claim) GetStreamSizeByMagic() (streamSize uint64, e error) { + if c.Value.GetStream() == nil { + return 0, errors.Err("this claim is not a stream") + } + resp, err := http.Get(reflectorURL + hex.EncodeToString(c.Value.GetStream().Source.SdHash)) + if err != nil { + return 0, errors.Err(err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return 0, errors.Err(err) + } + sdb := &stream.SDBlob{} + err = sdb.UnmarshalJSON(body) + + if err != nil { + return 0, err + } + lastBlobIndex := len(sdb.BlobInfos) - 2 + lastBlobHash := sdb.BlobInfos[lastBlobIndex].BlobHash + + if len(sdb.BlobInfos) > 2 { + streamSize = uint64(stream.MaxBlobSize-1) * uint64(len(sdb.BlobInfos)-2) + } + + resp2, err := http.Get(reflectorURL + hex.EncodeToString(lastBlobHash)) + if err != nil { + return 0, errors.Err(err) + } + defer resp2.Body.Close() + + body2, err := ioutil.ReadAll(resp2.Body) + if err != nil { + return 0, errors.Err(err) + } + defer func() { + if r := recover(); r != nil { + e = errors.Err("recovered from DecryptBlob panic for blob %s", lastBlobHash) + } + }() + lastBlob, err := stream.DecryptBlob(body2, sdb.Key, sdb.BlobInfos[lastBlobIndex].IV) + if err != nil { + return 0, errors.Err(err) + } + + streamSize += uint64(len(lastBlob)) + return streamSize, nil +} diff --git a/extras/jsonrpc/daemon_test.go b/extras/jsonrpc/daemon_test.go index b66aba2..1794342 100644 --- a/extras/jsonrpc/daemon_test.go +++ b/extras/jsonrpc/daemon_test.go @@ -445,6 +445,26 @@ func TestClient_AccountCreate(t *testing.T) { prettyPrint(*account) } +func TestClient_AccountAdd(t *testing.T) { + d := NewClient("") + name := "test" + fmt.Sprintf("%d", time.Now().Unix()) + "@lbry.com" + pubKey := "tpubDA9GDAntyJu4hD3wU7175p7CuV6DWbYXfyb2HedBA3yuBp9HZ4n3QE4Ex6RHCSiEuVp2nKAL1Lzf2ZLo9ApaFgNaJjG6Xo1wB3iEeVbrDZp" + account, err := d.AccountAdd(name, nil, nil, &pubKey, util.PtrToBool(true), nil) + if err != nil { + t.Fatal(err) + return + } + if account.Name != name { + t.Errorf("account name mismatch, expected %q, got %q", name, account.Name) + return + } + if account.PublicKey != pubKey { + t.Errorf("public key mismatch, expected %q, got %q", name, account.Name) + return + } + prettyPrint(*account) +} + func TestClient_AccountRemove(t *testing.T) { d := NewClient("") createdAccount, err := d.AccountCreate("test"+fmt.Sprintf("%d", time.Now().Unix())+"@lbry.com", false) diff --git a/extras/jsonrpc/daemon_types.go b/extras/jsonrpc/daemon_types.go index 39be1d9..5246480 100644 --- a/extras/jsonrpc/daemon_types.go +++ b/extras/jsonrpc/daemon_types.go @@ -204,14 +204,21 @@ type Account struct { MaximumUsesPerAddress uint64 `json:"maximum_uses_per_address"` } `json:"receiving"` } `json:"address_generator"` - Certificates uint64 `json:"certificates"` - Coins float64 `json:"coins"` - Encrypted bool `json:"encrypted"` - ID string `json:"id"` - IsDefault bool `json:"is_default"` - Name string `json:"name"` - PublicKey string `json:"public_key"` - Satoshis uint64 `json:"satoshis"` + Certificates uint64 `json:"certificates"` + Coins float64 `json:"coins"` + Encrypted bool `json:"encrypted"` + ID string `json:"id"` + IsDefault bool `json:"is_default"` + Ledger *string `json:"ledger,omitempty"` + ModifiedOn *float64 `json:"modified_on,omitempty"` + Name string `json:"name"` + Preferences *struct { + Theme string `json:"theme"` + } `json:"preferences,omitempty"` + PublicKey string `json:"public_key"` + PrivateKey *string `json:"private_key,omitempty"` + Seed *string `json:"seed,omitempty"` + Satoshis uint64 `json:"satoshis"` } type AccountListResponse struct { @@ -227,18 +234,6 @@ type AccountBalanceResponse struct { Total decimal.Decimal `json:"total"` } -type AccountCreateResponse struct { - ID string `json:"id"` - Name string `json:"name"` - PublicKey string `json:"public_key"` - PrivateKey string `json:"private_key"` - Seed string `json:"seed"` - Ledger string `json:"ledger"` - ModifiedOn float64 `json:"modified_on"` -} - -type AccountRemoveResponse AccountCreateResponse - type Transaction struct { Address string `json:"address"` Amount string `json:"amount"`