Skip to content
Snippets Groups Projects
Unverified Commit b16f9cac authored by Silas Davis's avatar Silas Davis
Browse files

Account: restructure Account type into mutable and immutable interfaces, update binary

package adding word160 for Address type, represent private accounts and validators,
use uint64 for heights and balances, update go-crypto interfaces.

Signed-off-by: default avatarSilas Davis <silas@monax.io>
parent 2e0c0357
No related branches found
No related tags found
No related merge requests found
Showing with 1367 additions and 149 deletions
......@@ -14,19 +14,14 @@
package account
// TODO: [ben] Account and PrivateAccount need to become a pure interface
// and then move the implementation to the manager types.
// Eg, Geth has its accounts, different from BurrowMint
import (
"bytes"
"encoding/json"
"fmt"
"io"
"github.com/hyperledger/burrow/common/sanity"
"github.com/hyperledger/burrow/binary"
ptypes "github.com/hyperledger/burrow/permission/types"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire"
)
......@@ -41,64 +36,319 @@ func SignBytes(chainID string, o Signable) []byte {
buf, n, err := new(bytes.Buffer), new(int), new(error)
o.WriteSignBytes(chainID, buf, n, err)
if *err != nil {
sanity.PanicCrisis(err)
panic(fmt.Sprintf("could not write sign bytes for a signable: %s", *err))
}
return buf.Bytes()
}
//-----------------------------------------------------------------------------
type Addressable interface {
// Get the 20 byte EVM address of this account
Address() Address
// Public key from which the Address is derived
PublicKey() PublicKey
}
// Account resides in the application state, and is mutated by transactions
// on the blockchain.
// Serialized by wire.[read|write]Reflect
type Account struct {
Address []byte `json:"address"`
PubKey crypto.PubKey `json:"pub_key"`
Sequence int `json:"sequence"`
Balance int64 `json:"balance"`
Code []byte `json:"code"` // VM code
StorageRoot []byte `json:"storage_root"` // VM storage merkle root.
// The default immutable interface to an account
type Account interface {
Addressable
// The value held by this account in terms of the chain-native token
Balance() uint64
// The EVM byte code held by this account (or equivalently, this contract)
Code() Bytecode
// The sequence number (or nonce) of this account, incremented each time a mutation of the
// Account is persisted to the blockchain state
Sequence() uint64
// The hash of all the values in this accounts storage (typically the root of some subtree
// in the merkle tree of global storage state)
StorageRoot() []byte
// The permission flags and roles for this account
Permissions() ptypes.AccountPermissions
// Obtain a deterministic serialisation of this account
// (i.e. update order and Go runtime independent)
Encode() []byte
}
Permissions ptypes.AccountPermissions `json:"permissions"`
type MutableAccount interface {
Account
// Set public key (needed for lazy initialisation), should also set the dependent address
SetPublicKey(pubKey PublicKey) MutableAccount
// Subtract amount from account balance (will panic if amount is greater than balance)
SubtractFromBalance(amount uint64) MutableAccount
// Add amount to balance (will panic if amount plus balance is a uint64 overflow)
AddToBalance(amount uint64) MutableAccount
// Set EVM byte code associated with account
SetCode(code []byte) MutableAccount
// Increment Sequence number by 1 (capturing the current Sequence number as the index for any pending mutations)
IncSequence() MutableAccount
// Set the storage root hash
SetStorageRoot(storageRoot []byte) MutableAccount
// Set account permissions
SetPermissions(permissions ptypes.AccountPermissions) MutableAccount
// Get a pointer this account's AccountPermissions in order to mutate them
MutablePermissions() *ptypes.AccountPermissions
// Create a complete copy of this MutableAccount that is itself mutable
Copy() MutableAccount
}
// -------------------------------------------------
// ConcreteAccount
// ConcreteAccount is the canonical serialisation and bash-in-place object for an Account
type ConcreteAccount struct {
Address Address
PublicKey PublicKey
Balance uint64
Code Bytecode
Sequence uint64
StorageRoot []byte
Permissions ptypes.AccountPermissions
}
func NewConcreteAccount(pubKey PublicKey) ConcreteAccount {
return ConcreteAccount{
Address: pubKey.Address(),
PublicKey: pubKey,
// Since nil slices and maps compare differently to empty ones
Code: Bytecode{},
StorageRoot: []byte{},
Permissions: ptypes.AccountPermissions{
Roles: []string{},
},
}
}
func (acc *Account) Copy() *Account {
func NewConcreteAccountFromSecret(secret string) ConcreteAccount {
return NewConcreteAccount(PublicKeyFromPubKey(PrivateKeyFromSecret(secret).PubKey()))
}
// Return as immutable Account
func (acc ConcreteAccount) Account() Account {
return concreteAccountWrapper{&acc}
}
// Return as mutable MutableAccount
func (acc ConcreteAccount) MutableAccount() MutableAccount {
return concreteAccountWrapper{&acc}
}
func (acc *ConcreteAccount) Encode() []byte {
return wire.BinaryBytes(acc)
}
func (acc *ConcreteAccount) Copy() *ConcreteAccount {
accCopy := *acc
return &accCopy
}
func (acc *Account) String() string {
func (acc *ConcreteAccount) String() string {
if acc == nil {
return "nil-Account"
return "Account{nil}"
}
return fmt.Sprintf("Account{%X:%v B:%v C:%v S:%X P:%s}", acc.Address, acc.PubKey, acc.Balance, len(acc.Code), acc.StorageRoot, acc.Permissions)
return fmt.Sprintf("Account{Address: %s; PublicKey: %v Balance: %v; CodeBytes: %v; StorageRoot: 0x%X; Permissions: %s}",
acc.Address, acc.PublicKey, acc.Balance, len(acc.Code), acc.StorageRoot, acc.Permissions)
}
func AccountEncoder(o interface{}, w io.Writer, n *int, err *error) {
wire.WriteBinary(o.(*Account), w, n, err)
// ConcreteAccount
// -------------------------------------------------
// Conversions
//
// Using the naming convention is this package of 'As<Type>' being
// a conversion from Account to <Type> and 'From<Type>' being conversion
// from <Type> to Account. Conversions are done by copying
// Returns a mutable, serialisable ConcreteAccount by copying from account
func AsConcreteAccount(account Account) *ConcreteAccount {
if account == nil {
return nil
}
if ca, ok := account.(concreteAccountWrapper); ok {
return ca.ConcreteAccount
}
return &ConcreteAccount{
Address: account.Address(),
PublicKey: account.PublicKey(),
Balance: account.Balance(),
Code: account.Code(),
Sequence: account.Sequence(),
StorageRoot: account.StorageRoot(),
Permissions: account.Permissions(),
}
}
// Creates an otherwise zeroed Account from an Addressable and returns it as MutableAccount
func FromAddressable(addressable Addressable) MutableAccount {
return ConcreteAccount{
Address: addressable.Address(),
PublicKey: addressable.PublicKey(),
// Since nil slices and maps compare differently to empty ones
Code: Bytecode{},
StorageRoot: []byte{},
Permissions: ptypes.AccountPermissions{
Roles: []string{},
},
}.MutableAccount()
}
// Returns an immutable account by copying from account
func AsAccount(account Account) Account {
if account == nil {
return nil
}
return AsConcreteAccount(account).Account()
}
// Returns a MutableAccount by copying from account
func AsMutableAccount(account Account) MutableAccount {
if account == nil {
return nil
}
return AsConcreteAccount(account).MutableAccount()
}
func GetMutableAccount(getter Getter, address Address) (MutableAccount, error) {
acc, err := getter.GetAccount(address)
if err != nil {
return nil, err
}
// If we get get our own concreteAccountWrapper back we can save an unnecessary copy and just
// return since ConcreteAccount.Account() will have been used to produce it which will already
// have copied
caw, ok := acc.(concreteAccountWrapper)
if ok {
return caw, nil
}
return AsMutableAccount(acc), nil
}
func AccountDecoder(r io.Reader, n *int, err *error) interface{} {
return wire.ReadBinary(&Account{}, r, 0, n, err)
//----------------------------------------------
// concreteAccount Wrapper
// concreteAccountWrapper wraps ConcreteAccount to provide a immutable read-only view
// via its implementation of Account and a mutable implementation via its implementation of
// MutableAccount
type concreteAccountWrapper struct {
*ConcreteAccount `json:"unwrap"`
}
var _ Account = concreteAccountWrapper{}
func (caw concreteAccountWrapper) Address() Address {
return caw.ConcreteAccount.Address
}
var AccountCodec = wire.Codec{
Encode: AccountEncoder,
Decode: AccountDecoder,
func (caw concreteAccountWrapper) PublicKey() PublicKey {
return caw.ConcreteAccount.PublicKey
}
func EncodeAccount(acc *Account) []byte {
w := new(bytes.Buffer)
var n int
var err error
AccountEncoder(acc, w, &n, &err)
return w.Bytes()
func (caw concreteAccountWrapper) Balance() uint64 {
return caw.ConcreteAccount.Balance
}
func DecodeAccount(accBytes []byte) *Account {
var n int
var err error
acc := AccountDecoder(bytes.NewBuffer(accBytes), &n, &err)
return acc.(*Account)
func (caw concreteAccountWrapper) Code() Bytecode {
return caw.ConcreteAccount.Code
}
func (caw concreteAccountWrapper) Sequence() uint64 {
return caw.ConcreteAccount.Sequence
}
func (caw concreteAccountWrapper) StorageRoot() []byte {
return caw.ConcreteAccount.StorageRoot
}
func (caw concreteAccountWrapper) Permissions() ptypes.AccountPermissions {
return caw.ConcreteAccount.Permissions
}
func (caw concreteAccountWrapper) Encode() []byte {
return caw.ConcreteAccount.Encode()
}
func (caw concreteAccountWrapper) MarshalJSON() ([]byte, error) {
return json.Marshal(caw.ConcreteAccount)
}
// Account mutation via MutableAccount interface
var _ MutableAccount = concreteAccountWrapper{}
func (caw concreteAccountWrapper) SetPublicKey(pubKey PublicKey) MutableAccount {
caw.ConcreteAccount.PublicKey = pubKey
addressFromPubKey := pubKey.Address()
// We don't want the wrong public key to take control of an account so we panic here
if caw.ConcreteAccount.Address != addressFromPubKey {
panic(fmt.Errorf("attempt to set public key of account %s to %v, "+
"but that public key has address %s",
caw.ConcreteAccount.Address, pubKey, addressFromPubKey))
}
return caw
}
func (caw concreteAccountWrapper) SubtractFromBalance(amount uint64) MutableAccount {
if amount > caw.Balance() {
panic(fmt.Errorf("insufficient funds: attempt to subtract %v from the balance of %s",
amount, caw.ConcreteAccount))
}
caw.ConcreteAccount.Balance -= amount
return caw
}
func (caw concreteAccountWrapper) AddToBalance(amount uint64) MutableAccount {
if binary.IsUint64SumOverflow(caw.Balance(), amount) {
panic(fmt.Errorf("uint64 overflow: attempt to add %v to the balance of %s",
amount, caw.ConcreteAccount))
}
caw.ConcreteAccount.Balance += amount
return caw
}
func (caw concreteAccountWrapper) SetCode(code []byte) MutableAccount {
caw.ConcreteAccount.Code = code
return caw
}
func (caw concreteAccountWrapper) IncSequence() MutableAccount {
caw.ConcreteAccount.Sequence += 1
return caw
}
func (caw concreteAccountWrapper) SetStorageRoot(storageRoot []byte) MutableAccount {
caw.ConcreteAccount.StorageRoot = storageRoot
return caw
}
func (caw concreteAccountWrapper) SetPermissions(permissions ptypes.AccountPermissions) MutableAccount {
caw.ConcreteAccount.Permissions = permissions
return caw
}
func (caw concreteAccountWrapper) MutablePermissions() *ptypes.AccountPermissions {
return &caw.ConcreteAccount.Permissions
}
func (caw concreteAccountWrapper) Copy() MutableAccount {
return concreteAccountWrapper{caw.ConcreteAccount.Copy()}
}
var _ = wire.RegisterInterface(struct{ Account }{}, wire.ConcreteType{concreteAccountWrapper{}, 0x01})
// concreteAccount Wrapper
//----------------------------------------------
// Encoding/decoding
func Decode(accBytes []byte) (Account, error) {
ca, err := DecodeConcrete(accBytes)
if err != nil {
return nil, err
}
return ca.Account(), nil
}
func DecodeConcrete(accBytes []byte) (*ConcreteAccount, error) {
ca := new(concreteAccountWrapper)
err := wire.ReadBinaryBytes(accBytes, ca)
if err != nil {
return nil, fmt.Errorf("could not convert decoded account to *ConcreteAccount: %v", err)
}
return ca.ConcreteAccount, nil
}
// Copyright 2017 Monax Industries Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package account
import (
"testing"
"encoding/json"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/go-wire"
)
func TestAddress(t *testing.T) {
bs := []byte{
1, 2, 3, 4, 5,
1, 2, 3, 4, 5,
1, 2, 3, 4, 5,
1, 2, 3, 4, 5,
}
addr, err := AddressFromBytes(bs)
assert.NoError(t, err)
word256 := addr.Word256()
leadingZeroes := []byte{
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
}
assert.Equal(t, leadingZeroes, word256[:12])
addrFromWord256 := AddressFromWord256(word256)
assert.Equal(t, bs, addrFromWord256[:])
assert.Equal(t, addr, addrFromWord256)
}
func TestAccountSerialise(t *testing.T) {
type AccountContainingStruct struct {
Account Account
ChainID string
}
// This test is really testing this go wire declaration in account.go
acc := NewConcreteAccountFromSecret("Super Semi Secret")
// Go wire cannot serialise a top-level interface type it needs to be a field or sub-field of a struct
// at some depth. i.e. you MUST wrap an interface if you want it to be decoded (they do not document this)
var accStruct = AccountContainingStruct{
Account: acc.Account(),
ChainID: "TestChain",
}
// We will write into this
accStructOut := AccountContainingStruct{}
// We must pass in a value type to read from (accStruct), but provide a pointer type to write into (accStructout
wire.ReadBinaryBytes(wire.BinaryBytes(accStruct), &accStructOut)
assert.Equal(t, accStruct, accStructOut)
}
func TestDecodeConcrete(t *testing.T) {
concreteAcc := NewConcreteAccountFromSecret("Super Semi Secret")
acc := concreteAcc.Account()
encodedAcc := acc.Encode()
concreteAccOut, err := DecodeConcrete(encodedAcc)
require.NoError(t, err)
assert.Equal(t, concreteAcc, *concreteAccOut)
concreteAccOut, err = DecodeConcrete([]byte("flungepliffery munknut tolopops"))
assert.Error(t, err)
}
func TestDecode(t *testing.T) {
concreteAcc := NewConcreteAccountFromSecret("Super Semi Secret")
acc := concreteAcc.Account()
accOut, err := Decode(acc.Encode())
assert.NoError(t, err)
assert.Equal(t, concreteAcc, *AsConcreteAccount(accOut))
accOut, err = Decode([]byte("flungepliffery munknut tolopops"))
assert.Error(t, err)
assert.Nil(t, accOut)
}
func TestMarshalJSON(t *testing.T) {
concreteAcc := NewConcreteAccountFromSecret("Super Semi Secret")
concreteAcc.Code = []byte{60, 23, 45}
acc := concreteAcc.Account()
bs, err := json.Marshal(acc)
assert.Equal(t, `{"Address":"745BD6BE33020146E04FA0F293A41E389887DE86","PublicKey":{"type":"ed25519","data":"8CEBC16C166A0614AD7C8E330318E774E1A039321F17274DF12ABA3B1BFC773C"},"Balance":0,"Code":"3C172D","Sequence":0,"StorageRoot":"","Permissions":{"Base":{"Perms":0,"SetBit":0},"Roles":[]}}`,
string(bs))
assert.NoError(t, err)
}
package account
import (
"encoding/json"
"fmt"
"github.com/hyperledger/burrow/binary"
"github.com/tmthrgd/go-hex"
"golang.org/x/crypto/ripemd160"
)
type Address binary.Word160
var ZeroAddress = Address{}
func AddressFromBytes(addr []byte) (address Address, err error) {
if len(addr) != binary.Word160Length {
err = fmt.Errorf("slice passed as address '%X' has %d bytes but should have %d bytes",
addr, len(addr), binary.Word160Length)
// It is caller's responsibility to check for errors. If they ignore the error we'll assume they want the
// best-effort mapping of the bytes passed to an address so we don't return here
}
copy(address[:], addr)
return
}
func AddressFromHexString(str string) (Address, error) {
bs, err := hex.DecodeString(str)
if err != nil {
return ZeroAddress, err
}
return AddressFromBytes(bs)
}
func MustAddressFromBytes(addr []byte) Address {
address, err := AddressFromBytes(addr)
if err != nil {
panic(fmt.Errorf("error reading address from bytes that caller does not expect: %s", err))
}
return address
}
func AddressFromWord256(addr binary.Word256) Address {
return Address(addr.Word160())
}
func (address Address) Word256() binary.Word256 {
return binary.Word160(address).Word256()
}
// Copy address and return a slice onto the copy
func (address Address) Bytes() []byte {
addressCopy := address
return addressCopy[:]
}
func (address Address) String() string {
return hex.EncodeUpperToString(address[:])
}
func (address *Address) UnmarshalJSON(data []byte) error {
str := new(string)
err := json.Unmarshal(data, str)
if err != nil {
return err
}
_, err = hex.Decode(address[:], []byte(*str))
if err != nil {
return err
}
return nil
}
func (address Address) MarshalJSON() ([]byte, error) {
return json.Marshal(hex.EncodeUpperToString(address[:]))
}
func (address *Address) UnmarshalText(text []byte) error {
_, err := hex.Decode(address[:], text)
return err
}
func (address Address) MarshalText() ([]byte, error) {
return ([]byte)(hex.EncodeUpperToString(address[:])), nil
}
func NewContractAddress(caller Address, sequence uint64) (newAddr Address) {
temp := make([]byte, 32+8)
copy(temp, caller[:])
binary.PutUint64BE(temp[32:], uint64(sequence))
hasher := ripemd160.New()
hasher.Write(temp) // does not error
copy(newAddr[:], hasher.Sum(nil))
return
}
package account
import (
"testing"
"encoding/json"
"github.com/stretchr/testify/assert"
)
func TestNewContractAddress(t *testing.T) {
addr := NewContractAddress(Address{
233, 181, 216, 115, 19,
53, 100, 101, 250, 227,
60, 64, 108, 226, 194,
151, 157, 230, 11, 203,
}, 1)
assert.Equal(t, Address{
73, 234, 48, 252, 174,
115, 27, 222, 54, 116,
47, 133, 144, 21, 73,
245, 21, 234, 26, 50,
}, addr)
}
func TestAddress_MarshalJSON(t *testing.T) {
addr := Address{
73, 234, 48, 252, 174,
115, 27, 222, 54, 116,
47, 133, 144, 21, 73,
245, 21, 234, 26, 50,
}
bs, err := json.Marshal(addr)
assert.NoError(t, err)
addrOut := new(Address)
err = json.Unmarshal(bs, addrOut)
assert.Equal(t, addr, *addrOut)
}
func TestAddress_MarshalText(t *testing.T) {
addr := Address{
73, 234, 48, 252, 174,
115, 27, 222, 54, 116,
47, 133, 144, 21, 73,
245, 21, 234, 26, 50,
}
bs, err := addr.MarshalText()
assert.NoError(t, err)
addrOut := new(Address)
err = addrOut.UnmarshalText(bs)
assert.Equal(t, addr, *addrOut)
}
package account
import (
"encoding/json"
"github.com/tmthrgd/go-hex"
)
// TODO: write a simple lexer that prints the opcodes. Each byte is either an OpCode or part of the Word256 argument
// to Push[1...32]
type Bytecode []byte
func (bc Bytecode) Bytes() []byte {
return bc[:]
}
func (bc Bytecode) String() string {
return hex.EncodeToString(bc[:])
}
func (bc Bytecode) MarshalJSON() ([]byte, error) {
return json.Marshal(hex.EncodeUpperToString(bc[:]))
}
func (bc Bytecode) UnmarshalJSON(data []byte) error {
str := new(string)
err := json.Unmarshal(data, str)
if err != nil {
return err
}
_, err = hex.Decode(bc[:], []byte(*str))
if err != nil {
return err
}
return nil
}
package account
import "github.com/tendermint/go-crypto"
// This allows us to control serialisation
type PublicKey struct {
crypto.PubKey `json:"unwrap"`
}
func PublicKeyFromPubKey(pubKey crypto.PubKey) PublicKey {
return PublicKey{PubKey: pubKey}
}
func PrivateKeyFromPrivKey(privKey crypto.PrivKey) PrivateKey {
return PrivateKey{PrivKey: privKey}
}
func (pk PublicKey) Address() Address {
return MustAddressFromBytes(pk.PubKey.Address())
}
func (pk PublicKey) MarshalJSON() ([]byte, error) {
return pk.PubKey.MarshalJSON()
}
func (pk *PublicKey) UnmarshalJSON(data []byte) error {
return pk.PubKey.UnmarshalJSON(data)
}
func (pk PublicKey) MarshalText() ([]byte, error) {
return pk.MarshalJSON()
}
func (pk *PublicKey) UnmarshalText(text []byte) error {
return pk.UnmarshalJSON(text)
}
type PrivateKey struct {
crypto.PrivKey
}
func (pk PrivateKey) PublicKey() PublicKey {
return PublicKeyFromPubKey(pk.PubKey())
}
func (pk PrivateKey) MarshalJSON() ([]byte, error) {
return pk.PrivKey.MarshalJSON()
}
func (pk *PrivateKey) UnmarshalJSON(data []byte) error {
return pk.PrivKey.UnmarshalJSON(data)
}
func (pk PrivateKey) MarshalText() ([]byte, error) {
return pk.MarshalJSON()
}
func (pk *PrivateKey) UnmarshalText(text []byte) error {
return pk.UnmarshalJSON(text)
}
// Copyright 2017 Monax Industries Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package account
// TODO: [ben] Account and PrivateAccount need to become a pure interface
// and then move the implementation to the manager types.
// Eg, Geth has its accounts, different from BurrowMint
import (
"fmt"
"github.com/hyperledger/burrow/common/sanity"
"github.com/tendermint/ed25519"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire"
)
type PrivAccount struct {
Address []byte `json:"address"`
PubKey crypto.PubKey `json:"pub_key"`
PrivKey crypto.PrivKey `json:"priv_key"`
}
func (pA *PrivAccount) Generate(index int) *PrivAccount {
newPrivKey := pA.PrivKey.(crypto.PrivKeyEd25519).Generate(index)
newPubKey := newPrivKey.PubKey()
newAddress := newPubKey.Address()
return &PrivAccount{
Address: newAddress,
PubKey: newPubKey,
PrivKey: newPrivKey,
}
}
func (pA *PrivAccount) Sign(chainID string, o Signable) crypto.Signature {
return pA.PrivKey.Sign(SignBytes(chainID, o))
}
func (pA *PrivAccount) String() string {
return fmt.Sprintf("PrivAccount{%X}", pA.Address)
}
//----------------------------------------
// Generates a new account with private key.
func GenPrivAccount() *PrivAccount {
privKeyBytes := new([64]byte)
copy(privKeyBytes[:32], crypto.CRandBytes(32))
pubKeyBytes := ed25519.MakePublicKey(privKeyBytes)
pubKey := crypto.PubKeyEd25519(*pubKeyBytes)
privKey := crypto.PrivKeyEd25519(*privKeyBytes)
return &PrivAccount{
Address: pubKey.Address(),
PubKey: pubKey,
PrivKey: privKey,
}
}
// Generates 32 priv key bytes from secret
func GenPrivKeyBytesFromSecret(secret string) []byte {
return wire.BinarySha256(secret) // Not Ripemd160 because we want 32 bytes.
}
// Generates a new account with private key from SHA256 hash of a secret
func GenPrivAccountFromSecret(secret string) *PrivAccount {
privKey32 := GenPrivKeyBytesFromSecret(secret)
privKeyBytes := new([64]byte)
copy(privKeyBytes[:32], privKey32)
pubKeyBytes := ed25519.MakePublicKey(privKeyBytes)
pubKey := crypto.PubKeyEd25519(*pubKeyBytes)
privKey := crypto.PrivKeyEd25519(*privKeyBytes)
return &PrivAccount{
Address: pubKey.Address(),
PubKey: pubKey,
PrivKey: privKey,
}
}
func GenPrivAccountFromPrivKeyBytes(privKeyBytes []byte) *PrivAccount {
if len(privKeyBytes) != 64 {
sanity.PanicSanity(fmt.Sprintf("Expected 64 bytes but got %v", len(privKeyBytes)))
}
var privKeyArray [64]byte
copy(privKeyArray[:], privKeyBytes)
pubKeyBytes := ed25519.MakePublicKey(&privKeyArray)
pubKey := crypto.PubKeyEd25519(*pubKeyBytes)
privKey := crypto.PrivKeyEd25519(privKeyArray)
return &PrivAccount{
Address: pubKey.Address(),
PubKey: pubKey,
PrivKey: privKey,
}
}
// Copyright 2017 Monax Industries Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package account
import (
"fmt"
"github.com/tendermint/ed25519"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire"
)
type Signer interface {
Sign(msg []byte) (crypto.Signature, error)
}
type PrivateAccount interface {
Addressable
PrivateKey() PrivateKey
Signer
}
type ConcretePrivateAccount struct {
Address Address
PublicKey PublicKey
PrivateKey PrivateKey
}
type concretePrivateAccountWrapper struct {
*ConcretePrivateAccount `json:"unwrap"`
}
var _ PrivateAccount = concretePrivateAccountWrapper{}
var _ = wire.RegisterInterface(struct{ PrivateAccount }{}, wire.ConcreteType{concretePrivateAccountWrapper{}, 0x01})
func (cpaw concretePrivateAccountWrapper) Address() Address {
return cpaw.ConcretePrivateAccount.Address
}
func (cpaw concretePrivateAccountWrapper) PublicKey() PublicKey {
return cpaw.ConcretePrivateAccount.PublicKey
}
func (cpaw concretePrivateAccountWrapper) PrivateKey() PrivateKey {
return cpaw.ConcretePrivateAccount.PrivateKey
}
func (pa ConcretePrivateAccount) PrivateAccount() concretePrivateAccountWrapper {
return concretePrivateAccountWrapper{&pa}
}
func (pa ConcretePrivateAccount) Sign(msg []byte) (crypto.Signature, error) {
return pa.PrivateKey.Sign(msg), nil
}
func ChainSign(pa PrivateAccount, chainID string, o Signable) crypto.Signature {
sig, _ := pa.Sign(SignBytes(chainID, o))
return sig
}
func (pa *ConcretePrivateAccount) Generate(index int) concretePrivateAccountWrapper {
newPrivKey := PrivateKeyFromPrivKey(pa.PrivateKey.Unwrap().(crypto.PrivKeyEd25519).Generate(index).Wrap())
newPubKey := PublicKeyFromPubKey(newPrivKey.PubKey())
newAddress := newPubKey.Address()
return ConcretePrivateAccount{
Address: newAddress,
PublicKey: newPubKey,
PrivateKey: newPrivKey,
}.PrivateAccount()
}
func (pa *ConcretePrivateAccount) String() string {
return fmt.Sprintf("ConcretePrivateAccount{%s}", pa.Address)
}
//----------------------------------------
// Generates a new account with private key.
func GeneratePrivateAccount() concretePrivateAccountWrapper {
privKeyBytes := new([64]byte)
copy(privKeyBytes[:32], crypto.CRandBytes(32))
pubKeyBytes := ed25519.MakePublicKey(privKeyBytes)
publicKey := PublicKeyFromPubKey(crypto.PubKeyEd25519(*pubKeyBytes).Wrap())
address := publicKey.Address()
privateKey := PrivateKeyFromPrivKey(crypto.PrivKeyEd25519(*privKeyBytes).Wrap())
return ConcretePrivateAccount{
Address: address,
PublicKey: publicKey,
PrivateKey: privateKey,
}.PrivateAccount()
}
func PrivateKeyFromSecret(secret string) PrivateKey {
return PrivateKeyFromPrivKey(crypto.GenPrivKeyEd25519FromSecret(wire.BinarySha256(secret)).Wrap())
}
// Generates a new account with private key from SHA256 hash of a secret
func GeneratePrivateAccountFromSecret(secret string) concretePrivateAccountWrapper {
privKey := PrivateKeyFromSecret(secret)
pubKey := PublicKeyFromPubKey(privKey.PubKey())
return ConcretePrivateAccount{
Address: pubKey.Address(),
PublicKey: pubKey,
PrivateKey: privKey,
}.PrivateAccount()
}
func GeneratePrivateAccountFromPrivateKeyBytes(privKeyBytes []byte) concretePrivateAccountWrapper {
if len(privKeyBytes) != 64 {
panic(fmt.Sprintf("Expected 64 bytes but got %v", len(privKeyBytes)))
}
var privKeyArray [64]byte
copy(privKeyArray[:], privKeyBytes)
pubKeyBytes := ed25519.MakePublicKey(&privKeyArray)
publicKey := PublicKeyFromPubKey(crypto.PubKeyEd25519(*pubKeyBytes).Wrap())
address := publicKey.Address()
privateKey := PrivateKeyFromPrivKey(crypto.PrivKeyEd25519(privKeyArray).Wrap())
return ConcretePrivateAccount{
Address: address,
PublicKey: publicKey,
PrivateKey: privateKey,
}.PrivateAccount()
}
// Copyright 2017 Monax Industries Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package account
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tendermint/go-wire"
"github.com/stretchr/testify/require"
)
func TestPrivateAccountSerialise(t *testing.T) {
type PrivateAccountContainingStruct struct {
PrivateAccount PrivateAccount
ChainID string
}
// This test is really testing this go wire declaration in private_account.go
acc := GeneratePrivateAccountFromSecret("Super Secret Secret")
// Go wire cannot serialise a top-level interface type it needs to be a field or sub-field of a struct
// at some depth. i.e. you MUST wrap an interface if you want it to be decoded (they do not document this)
var accStruct = PrivateAccountContainingStruct{
PrivateAccount: acc,
ChainID: "TestChain",
}
// We will write into this
accStructOut := PrivateAccountContainingStruct{}
// We must pass in a value type to read from (accStruct), but provide a pointer type to write into (accStructout
err := wire.ReadBinaryBytes(wire.BinaryBytes(accStruct), &accStructOut)
require.NoError(t, err)
assert.Equal(t, accStruct, accStructOut)
}
package account
import (
"github.com/hyperledger/burrow/binary"
)
type Getter interface {
// Get an account by its address return nil if it does not exist (which should not be an error)
GetAccount(address Address) (Account, error)
}
type Iterable interface {
// Iterates through accounts calling passed function once per account, if the consumer
// returns true the iteration breaks and returns true to indicate it iteration
// was escaped
IterateAccounts(consumer func(Account) (stop bool)) (stopped bool, err error)
}
type Updater interface {
// Updates the fields of updatedAccount by address, creating the account
// if it does not exist
UpdateAccount(updatedAccount Account) error
// Remove the account at address
RemoveAccount(address Address) error
}
type StorageGetter interface {
// Retrieve a 32-byte value stored at key for the account at address, return Zero256 if key does not exist but
// error if address does not
GetStorage(address Address, key binary.Word256) (value binary.Word256, err error)
}
type StorageSetter interface {
// Store a 32-byte value at key for the account at address
SetStorage(address Address, key, value binary.Word256) error
}
type StorageIterable interface {
// Iterates through the storage of account ad address calling the passed function once per account,
// if the iterator function returns true the iteration breaks and returns true to indicate it iteration
// was escaped
IterateStorage(address Address, consumer func(key, value binary.Word256) (stop bool)) (stopped bool, err error)
}
// Compositions
// Read-only account and storage state
type StateReader interface {
Getter
StorageGetter
}
// Read and list account and storage state
type StateIterable interface {
StateReader
Iterable
StorageIterable
}
// Read and write account and storage state
type StateWriter interface {
StateReader
Updater
StorageSetter
}
package account
import (
"encoding/json"
)
type Validator interface {
Addressable
// The validator's voting power
Power() uint64
// Alter the validator's voting power by amount that can be negative or positive.
// A power of 0 effectively unbonds the validator
WithNewPower(uint64) Validator
}
// Neither abci_types or tm_types has quite the representation we want
type ConcreteValidator struct {
Address Address
PublicKey PublicKey
Power uint64
}
type concreteValidatorWrapper struct {
*ConcreteValidator `json:"unwrap"`
}
var _ Validator = concreteValidatorWrapper{}
func AsValidator(account Account) Validator {
return ConcreteValidator{
Address: account.Address(),
PublicKey: account.PublicKey(),
Power: account.Balance(),
}.Validator()
}
func AsConcreteValidator(validator Validator) *ConcreteValidator {
if validator == nil {
return nil
}
if ca, ok := validator.(concreteValidatorWrapper); ok {
return ca.ConcreteValidator
}
return &ConcreteValidator{
Address: validator.Address(),
PublicKey: validator.PublicKey(),
Power: validator.Power(),
}
}
func (cvw concreteValidatorWrapper) Address() Address {
return cvw.ConcreteValidator.Address
}
func (cvw concreteValidatorWrapper) PublicKey() PublicKey {
return cvw.ConcreteValidator.PublicKey
}
func (cvw concreteValidatorWrapper) Power() uint64 {
return cvw.ConcreteValidator.Power
}
func (cvw concreteValidatorWrapper) WithNewPower(power uint64) Validator {
cv := cvw.Copy()
cv.Power = power
return concreteValidatorWrapper{
ConcreteValidator: cv,
}
}
func (cv ConcreteValidator) Validator() Validator {
return concreteValidatorWrapper{
ConcreteValidator: &cv,
}
}
func (cv *ConcreteValidator) Copy() *ConcreteValidator {
cvCopy := *cv
return &cvCopy
}
func (cv *ConcreteValidator) String() string {
if cv == nil {
return "Nil Validator"
}
bs, err := json.Marshal(cv)
if err != nil {
return "error serialising Validator"
}
return string(bs)
}
package account
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestAlterPower(t *testing.T) {
val := AsValidator(NewConcreteAccountFromSecret("seeeeecret").Account())
valInc := val.WithNewPower(2442132)
assert.Equal(t, uint64(0), val.Power())
assert.Equal(t, uint64(2442132), valInc.Power())
}
// Copyright 2017 Monax Industries Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package binary
func Fingerprint(slice []byte) []byte {
fingerprint := make([]byte, 6)
copy(fingerprint, slice)
return fingerprint
}
func IsZeros(slice []byte) bool {
for _, byt := range slice {
if byt != byte(0) {
return false
}
}
return true
}
func RightPadBytes(slice []byte, l int) []byte {
if l < len(slice) {
return slice
}
padded := make([]byte, l)
copy(padded[0:len(slice)], slice)
return padded
}
func LeftPadBytes(slice []byte, l int) []byte {
if l < len(slice) {
return slice
}
padded := make([]byte, l)
copy(padded[l-len(slice):], slice)
return padded
}
// Copyright 2017 Monax Industries Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package binary
import (
"encoding/binary"
"math"
"sort"
)
const Uint64TopBitMask = 1 << 63
// Sort for []uint64
type Uint64Slice []uint64
func (p Uint64Slice) Len() int { return len(p) }
func (p Uint64Slice) Less(i, j int) bool { return p[i] < p[j] }
func (p Uint64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p Uint64Slice) Sort() { sort.Sort(p) }
func SearchUint64s(a []uint64, x uint64) int {
return sort.Search(len(a), func(i int) bool { return a[i] >= x })
}
func (p Uint64Slice) Search(x uint64) int { return SearchUint64s(p, x) }
//--------------------------------------------------------------------------------
func PutUint64LE(dest []byte, i uint64) {
binary.LittleEndian.PutUint64(dest, i)
}
func GetUint64LE(src []byte) uint64 {
return binary.LittleEndian.Uint64(src)
}
func PutUint64BE(dest []byte, i uint64) {
binary.BigEndian.PutUint64(dest, i)
}
func GetUint64BE(src []byte) uint64 {
return binary.BigEndian.Uint64(src)
}
func PutInt64LE(dest []byte, i int64) {
binary.LittleEndian.PutUint64(dest, uint64(i))
}
func GetInt64LE(src []byte) int64 {
return int64(binary.LittleEndian.Uint64(src))
}
func PutInt64BE(dest []byte, i int64) {
binary.BigEndian.PutUint64(dest, uint64(i))
}
func GetInt64BE(src []byte) int64 {
return int64(binary.BigEndian.Uint64(src))
}
// Returns whether a + b would be a uint64 overflow
func IsUint64SumOverflow(a, b uint64) bool {
return math.MaxUint64-a < b
}
package binary
import (
"math"
"testing"
"github.com/stretchr/testify/assert"
)
func TestIsUint64SumOverflow(t *testing.T) {
var b uint64 = 0xdeadbeef
var a uint64 = math.MaxUint64 - b
assert.False(t, IsUint64SumOverflow(a-b, b))
assert.False(t, IsUint64SumOverflow(a, b))
assert.False(t, IsUint64SumOverflow(a+b, 0))
assert.True(t, IsUint64SumOverflow(a, b+1))
assert.True(t, IsUint64SumOverflow(a+b, 1))
assert.True(t, IsUint64SumOverflow(a+1, b+1))
}
package binary
const Word160Length = 20
const Word256Word160Delta = 12
var Zero160 = Word160{}
type Word160 [Word160Length]byte
// Pad a Word160 on the left and embed it in a Word256 (as it is for account addresses in EVM)
func (w Word160) Word256() (word256 Word256) {
copy(word256[Word256Word160Delta:], w[:])
return
}
package binary
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestWord160_Word256(t *testing.T) {
word256 := Word256{
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
1, 2, 3, 4, 5,
6, 7, 8, 9, 10,
11, 12, 13, 14, 15,
16, 17, 18, 19, 20,
}
word160 := Word160{
1, 2, 3, 4, 5,
6, 7, 8, 9, 10,
11, 12, 13, 14, 15,
16, 17, 18, 19, 20,
}
assert.Equal(t, word256, word160.Word256())
assert.Equal(t, word160, word256.Word160())
}
// Copyright 2017 Monax Industries Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package binary
import (
"bytes"
"sort"
)
var (
Zero256 = Word256{}
One256 = Word256{1}
)
const Word256Length = 32
var trimCutSet = string([]byte{0})
type Word256 [Word256Length]byte
func (w Word256) String() string {
return string(w[:])
}
func (w Word256) Copy() Word256 {
return w
}
func (w Word256) Bytes() []byte {
return w[:]
}
// copied.
func (w Word256) Prefix(n int) []byte {
return w[:n]
}
func (w Word256) Postfix(n int) []byte {
return w[32-n:]
}
// Get a Word160 embedded a Word256 and padded on the left (as it is for account addresses in EVM)
func (w Word256) Word160() (w160 Word160) {
copy(w160[:], w[Word256Word160Delta:])
return
}
func (w Word256) IsZero() bool {
accum := byte(0)
for _, byt := range w {
accum |= byt
}
return accum == 0
}
func (w Word256) Compare(other Word256) int {
return bytes.Compare(w[:], other[:])
}
func (w Word256) UnpadLeft() []byte {
return bytes.TrimLeft(w[:], trimCutSet)
}
func (w Word256) UnpadRight() []byte {
return bytes.TrimRight(w[:], trimCutSet)
}
func Uint64ToWord256(i uint64) Word256 {
buf := [8]byte{}
PutUint64BE(buf[:], i)
return LeftPadWord256(buf[:])
}
func Int64ToWord256(i int64) Word256 {
buf := [8]byte{}
PutInt64BE(buf[:], i)
return LeftPadWord256(buf[:])
}
func RightPadWord256(bz []byte) (word Word256) {
copy(word[:], bz)
return
}
func LeftPadWord256(bz []byte) (word Word256) {
copy(word[32-len(bz):], bz)
return
}
func Uint64FromWord256(word Word256) uint64 {
buf := word.Postfix(8)
return GetUint64BE(buf)
}
func Int64FromWord256(word Word256) int64 {
buf := word.Postfix(8)
return GetInt64BE(buf)
}
//-------------------------------------
type Tuple256 struct {
First Word256
Second Word256
}
func (tuple Tuple256) Compare(other Tuple256) int {
firstCompare := tuple.First.Compare(other.First)
if firstCompare == 0 {
return tuple.Second.Compare(other.Second)
} else {
return firstCompare
}
}
func Tuple256Split(t Tuple256) (Word256, Word256) {
return t.First, t.Second
}
type Tuple256Slice []Tuple256
func (p Tuple256Slice) Len() int { return len(p) }
func (p Tuple256Slice) Less(i, j int) bool {
return p[i].Compare(p[j]) < 0
}
func (p Tuple256Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p Tuple256Slice) Sort() { sort.Sort(p) }
package binary
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestWord256_UnpadLeft(t *testing.T) {
bs := []byte{0x45, 0x12}
w := LeftPadWord256(bs)
wExpected := Word256{}
wExpected[30] = bs[0]
wExpected[31] = bs[1]
assert.Equal(t, wExpected, w)
assert.Equal(t, bs, w.UnpadLeft())
}
func TestWord256_UnpadRight(t *testing.T) {
bs := []byte{0x45, 0x12}
w := RightPadWord256(bs)
wExpected := Word256{}
wExpected[0] = bs[0]
wExpected[1] = bs[1]
assert.Equal(t, wExpected, w)
assert.Equal(t, bs, w.UnpadRight())
}
func TestLeftPadWord256(t *testing.T) {
assert.Equal(t, Zero256, LeftPadWord256(nil))
assert.Equal(t,
Word256{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 2, 3,
},
LeftPadWord256([]byte{1, 2, 3}))
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment