2018-03-02 15:24:09 +00:00
|
|
|
package wallet
|
|
|
|
|
2019-08-27 13:29:42 +00:00
|
|
|
import (
|
2020-01-15 13:36:57 +00:00
|
|
|
"encoding/hex"
|
|
|
|
"encoding/json"
|
2020-01-09 15:30:25 +00:00
|
|
|
"errors"
|
|
|
|
|
2020-01-15 15:08:25 +00:00
|
|
|
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
2019-08-27 13:29:42 +00:00
|
|
|
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
2020-01-15 15:08:25 +00:00
|
|
|
"github.com/CityOfZion/neo-go/pkg/util"
|
2019-08-27 13:29:42 +00:00
|
|
|
)
|
2018-03-02 15:24:09 +00:00
|
|
|
|
|
|
|
// Account represents a NEO account. It holds the private and public key
|
|
|
|
// along with some metadata.
|
|
|
|
type Account struct {
|
|
|
|
// NEO private key.
|
2019-08-27 13:29:42 +00:00
|
|
|
privateKey *keys.PrivateKey
|
2018-03-02 15:24:09 +00:00
|
|
|
|
|
|
|
// NEO public key.
|
|
|
|
publicKey []byte
|
|
|
|
|
|
|
|
// Account import file.
|
|
|
|
wif string
|
|
|
|
|
2019-02-13 18:01:10 +00:00
|
|
|
// NEO public address.
|
2018-03-02 15:24:09 +00:00
|
|
|
Address string `json:"address"`
|
|
|
|
|
|
|
|
// Encrypted WIF of the account also known as the key.
|
|
|
|
EncryptedWIF string `json:"key"`
|
|
|
|
|
|
|
|
// Label is a label the user had made for this account.
|
|
|
|
Label string `json:"label"`
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// Contract is a Contract object which describes the details of the contract.
|
2018-03-02 15:24:09 +00:00
|
|
|
// This field can be null (for watch-only address).
|
|
|
|
Contract *Contract `json:"contract"`
|
|
|
|
|
|
|
|
// Indicates whether the account is locked by the user.
|
|
|
|
// the client shouldn't spend the funds in a locked account.
|
|
|
|
Locked bool `json:"lock"`
|
|
|
|
|
|
|
|
// Indicates whether the account is the default change account.
|
|
|
|
Default bool `json:"isDefault"`
|
|
|
|
}
|
|
|
|
|
2019-02-13 18:01:10 +00:00
|
|
|
// Contract represents a subset of the smartcontract to embed in the
|
2018-03-02 15:24:09 +00:00
|
|
|
// Account so it's NEP-6 compliant.
|
|
|
|
type Contract struct {
|
2020-01-15 13:36:57 +00:00
|
|
|
// Script of the contract deployed on the blockchain.
|
|
|
|
Script []byte `json:"script"`
|
2018-03-02 15:24:09 +00:00
|
|
|
|
|
|
|
// A list of parameters used deploying this contract.
|
|
|
|
Parameters []interface{} `json:"parameters"`
|
|
|
|
|
|
|
|
// Indicates whether the contract has been deployed to the blockchain.
|
|
|
|
Deployed bool `json:"deployed"`
|
|
|
|
}
|
|
|
|
|
2020-01-15 13:36:57 +00:00
|
|
|
// contract is an intermediate struct used for json unmarshalling.
|
|
|
|
type contract struct {
|
|
|
|
// Script is a hex-encoded script of the contract.
|
|
|
|
Script string `json:"script"`
|
|
|
|
|
|
|
|
// A list of parameters used deploying this contract.
|
|
|
|
Parameters []interface{} `json:"parameters"`
|
|
|
|
|
|
|
|
// Indicates whether the contract has been deployed to the blockchain.
|
|
|
|
Deployed bool `json:"deployed"`
|
|
|
|
}
|
|
|
|
|
2020-01-15 15:08:25 +00:00
|
|
|
// ScriptHash returns the hash of contract's script.
|
|
|
|
func (c Contract) ScriptHash() util.Uint160 {
|
|
|
|
return hash.Hash160(c.Script)
|
|
|
|
}
|
|
|
|
|
2020-01-15 13:36:57 +00:00
|
|
|
// MarshalJSON implements json.Marshaler interface.
|
|
|
|
func (c Contract) MarshalJSON() ([]byte, error) {
|
|
|
|
var cc contract
|
|
|
|
|
|
|
|
cc.Script = hex.EncodeToString(c.Script)
|
|
|
|
cc.Parameters = c.Parameters
|
|
|
|
cc.Deployed = c.Deployed
|
|
|
|
|
|
|
|
return json.Marshal(cc)
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalJSON implements json.Unmarshaler interface.
|
|
|
|
func (c *Contract) UnmarshalJSON(data []byte) error {
|
|
|
|
var cc contract
|
|
|
|
|
|
|
|
if err := json.Unmarshal(data, &cc); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
script, err := hex.DecodeString(cc.Script)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Script = script
|
|
|
|
c.Parameters = cc.Parameters
|
|
|
|
c.Deployed = cc.Deployed
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-03-02 15:24:09 +00:00
|
|
|
// NewAccount creates a new Account with a random generated PrivateKey.
|
|
|
|
func NewAccount() (*Account, error) {
|
2019-08-27 13:29:42 +00:00
|
|
|
priv, err := keys.NewPrivateKey()
|
2018-03-02 15:24:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-09-05 06:35:02 +00:00
|
|
|
return newAccountFromPrivateKey(priv), nil
|
2018-03-02 15:24:09 +00:00
|
|
|
}
|
|
|
|
|
2020-01-09 15:30:25 +00:00
|
|
|
// Decrypt decrypts the EncryptedWIF with the given passphrase returning error
|
|
|
|
// if anything goes wrong.
|
|
|
|
func (a *Account) Decrypt(passphrase string) error {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
if a.EncryptedWIF == "" {
|
|
|
|
return errors.New("no encrypted wif in the account")
|
2018-03-02 15:24:09 +00:00
|
|
|
}
|
2020-01-09 15:30:25 +00:00
|
|
|
a.privateKey, err = keys.NEP2Decrypt(a.EncryptedWIF, passphrase)
|
2020-02-20 10:06:10 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
a.publicKey = a.privateKey.PublicKey().Bytes()
|
|
|
|
a.wif = a.privateKey.WIF()
|
|
|
|
|
|
|
|
return nil
|
2018-03-02 15:24:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Encrypt encrypts the wallet's PrivateKey with the given passphrase
|
|
|
|
// under the NEP-2 standard.
|
|
|
|
func (a *Account) Encrypt(passphrase string) error {
|
2019-08-27 13:29:42 +00:00
|
|
|
wif, err := keys.NEP2Encrypt(a.privateKey, passphrase)
|
2018-03-02 15:24:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
a.EncryptedWIF = wif
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-11-15 10:32:40 +00:00
|
|
|
// PrivateKey returns private key corresponding to the account.
|
|
|
|
func (a *Account) PrivateKey() *keys.PrivateKey {
|
|
|
|
return a.privateKey
|
|
|
|
}
|
|
|
|
|
2018-03-02 15:24:09 +00:00
|
|
|
// NewAccountFromWIF creates a new Account from the given WIF.
|
|
|
|
func NewAccountFromWIF(wif string) (*Account, error) {
|
2019-08-27 13:29:42 +00:00
|
|
|
privKey, err := keys.NewPrivateKeyFromWIF(wif)
|
2018-03-02 15:24:09 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-09-05 06:35:02 +00:00
|
|
|
return newAccountFromPrivateKey(privKey), nil
|
2018-03-02 15:24:09 +00:00
|
|
|
}
|
|
|
|
|
2020-02-20 09:22:40 +00:00
|
|
|
// NewAccountFromEncryptedWIF creates a new Account from the given encrypted WIF.
|
|
|
|
func NewAccountFromEncryptedWIF(wif string, pass string) (*Account, error) {
|
|
|
|
priv, err := keys.NEP2Decrypt(wif, pass)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
a := newAccountFromPrivateKey(priv)
|
|
|
|
a.EncryptedWIF = wif
|
|
|
|
|
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
2019-10-22 14:56:03 +00:00
|
|
|
// newAccountFromPrivateKey creates a wallet from the given PrivateKey.
|
2019-09-05 06:35:02 +00:00
|
|
|
func newAccountFromPrivateKey(p *keys.PrivateKey) *Account {
|
|
|
|
pubKey := p.PublicKey()
|
|
|
|
pubAddr := p.Address()
|
|
|
|
wif := p.WIF()
|
2018-03-02 15:24:09 +00:00
|
|
|
|
|
|
|
a := &Account{
|
2019-02-19 18:37:35 +00:00
|
|
|
publicKey: pubKey.Bytes(),
|
2018-03-02 15:24:09 +00:00
|
|
|
privateKey: p,
|
|
|
|
Address: pubAddr,
|
|
|
|
wif: wif,
|
|
|
|
}
|
|
|
|
|
2019-09-05 06:35:02 +00:00
|
|
|
return a
|
2018-03-02 15:24:09 +00:00
|
|
|
}
|