From 7b8e4cde2442c6c8098a60aa32b2ac99cac0fee9 Mon Sep 17 00:00:00 2001 From: Silas Davis <silas@monax.io> Date: Thu, 19 Jul 2018 00:33:13 +0100 Subject: [PATCH] Implemented GovernanceContext to implement GovernanceTX used ValidatorRing to control validator power flow Signed-off-by: Silas Davis <silas@monax.io> --- acm/account.go | 25 +- acm/private_account.go | 2 +- acm/validator.go | 2 +- blockchain/blockchain.go | 74 ++-- blockchain/blockchain_test.go | 46 +++ blockchain/validators.go | 213 ++++++---- blockchain/validators_ring.go | 198 +++++++++ blockchain/validators_ring_test.go | 121 ++++++ blockchain/validators_test.go | 39 +- blockchain/validators_window.go | 64 --- cmd/burrow/commands/configure.go | 2 +- consensus/tendermint/abci/app.go | 81 ++-- .../validator/priv_validator_memory.go | 5 +- core/kernel.go | 5 +- crypto/address.go | 34 ++ crypto/crypto.go | 10 + crypto/crypto.pb.go | 62 +-- crypto/private_key.go | 22 +- crypto/public_key.go | 58 +-- .../{executors => contexts}/call_context.go | 2 +- execution/contexts/governance_context.go | 130 ++++++ .../{executors => contexts}/name_context.go | 2 +- .../permissions_context.go | 2 +- .../{executors => contexts}/send_context.go | 5 +- execution/{executors => contexts}/shared.go | 8 +- execution/evm/snative.go | 13 +- execution/exec/event.go | 2 + execution/exec/exec.pb.go | 390 +++++++++++++----- execution/exec/govern_account_event.go | 1 + execution/exec/tx_execution.go | 14 +- execution/execution.go | 57 ++- execution/execution_test.go | 22 +- execution/simulated_call.go | 4 +- execution/transactor_test.go | 2 +- genesis/deterministic_genesis.go | 2 +- genesis/genesis.go | 6 +- genesis/spec/genesis_spec.go | 2 +- genesis/spec/template_account.go | 2 +- integration/governance/main_test.go | 47 +++ integration/governance/validators_test.go | 7 + integration/integration.go | 13 +- integration/rpcevents/main_test.go | 18 +- integration/rpcquery/main_test.go | 18 +- integration/rpctransact/main_test.go | 18 +- integration/tm/main_test.go | 17 +- keys/server.go | 4 +- permission/perm_flag.go | 5 + protobuf/crypto.proto | 4 +- protobuf/exec.proto | 33 +- rpc/service.go | 9 +- txs/payload/governance_tx.go | 27 -- 51 files changed, 1377 insertions(+), 572 deletions(-) create mode 100644 blockchain/blockchain_test.go create mode 100644 blockchain/validators_ring.go create mode 100644 blockchain/validators_ring_test.go delete mode 100644 blockchain/validators_window.go rename execution/{executors => contexts}/call_context.go (99%) create mode 100644 execution/contexts/governance_context.go rename execution/{executors => contexts}/name_context.go (99%) rename execution/{executors => contexts}/permissions_context.go (99%) rename execution/{executors => contexts}/send_context.go (93%) rename execution/{executors => contexts}/shared.go (96%) create mode 100644 execution/exec/govern_account_event.go create mode 100644 integration/governance/main_test.go create mode 100644 integration/governance/validators_test.go diff --git a/acm/account.go b/acm/account.go index 4c3b1170..73d1a0a3 100644 --- a/acm/account.go +++ b/acm/account.go @@ -27,16 +27,9 @@ import ( var GlobalPermissionsAddress = crypto.Address(binary.Zero160) -type Addressable interface { - // Get the 20 byte EVM address of this account - Address() crypto.Address - // Public key from which the Address is derived - PublicKey() crypto.PublicKey -} - // The default immutable interface to an account type Account interface { - Addressable + crypto.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) @@ -117,7 +110,7 @@ func AsConcreteAccount(account Account) *ConcreteAccount { } // Creates an otherwise zeroed Account from an Addressable and returns it as MutableAccount -func FromAddressable(addressable Addressable) *MutableAccount { +func FromAddressable(addressable crypto.Addressable) *MutableAccount { ca := &ConcreteAccount{ Address: addressable.Address(), PublicKey: addressable.PublicKey(), @@ -138,6 +131,8 @@ func AsMutableAccount(account Account) *MutableAccount { return AsConcreteAccount(account).MutableAccount() } +var _ Account = &MutableAccount{} + func (acc ConcreteAccount) String() string { return fmt.Sprintf("ConcreteAccount{Address: %s; Sequence: %v; PublicKey: %v Balance: %v; CodeLength: %v; Permissions: %s}", acc.Address, acc.Sequence, acc.PublicKey, acc.Balance, len(acc.Code), acc.Permissions) @@ -177,6 +172,11 @@ func (acc *MutableAccount) AddToBalance(amount uint64) error { return nil } +func (acc *MutableAccount) SetBalance(amount uint64) error { + acc.concreteAccount.Balance = amount + return nil +} + func (acc *MutableAccount) SetCode(code []byte) error { acc.concreteAccount.Code = code return nil @@ -186,8 +186,11 @@ func (acc *MutableAccount) IncSequence() { acc.concreteAccount.Sequence++ } -func (acc *MutableAccount) SetPermissions(permissions permission.AccountPermissions) error { - acc.concreteAccount.Permissions = permissions +func (acc *MutableAccount) SetPermissions(accPerms permission.AccountPermissions) error { + if !accPerms.Base.Perms.IsValid() { + return fmt.Errorf("attempt to set invalid perm 0%b on account %v", accPerms.Base.Perms, acc) + } + acc.concreteAccount.Permissions = accPerms return nil } diff --git a/acm/private_account.go b/acm/private_account.go index f9cf705d..f6f80ba3 100644 --- a/acm/private_account.go +++ b/acm/private_account.go @@ -23,7 +23,7 @@ import ( ) type AddressableSigner interface { - Addressable + crypto.Addressable crypto.Signer } diff --git a/acm/validator.go b/acm/validator.go index 51027539..ddf471ec 100644 --- a/acm/validator.go +++ b/acm/validator.go @@ -7,7 +7,7 @@ import ( ) type Validator interface { - Addressable + crypto.Addressable // The validator's voting power Power() uint64 } diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 2d1f3553..4d8e2c94 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -16,15 +16,15 @@ package blockchain import ( "bytes" - "encoding/json" "fmt" - "time" - + "math/big" "sync" + "time" "github.com/hyperledger/burrow/crypto" "github.com/hyperledger/burrow/genesis" "github.com/hyperledger/burrow/logging" + "github.com/tendermint/go-amino" dbm "github.com/tendermint/tmlibs/db" ) @@ -39,7 +39,7 @@ type TipInfo interface { LastBlockTime() time.Time LastBlockHash() []byte AppHashAfterLastBlock() []byte - IterateValidators(iter func(publicKey crypto.PublicKey, power uint64) (stop bool)) (stopped bool) + IterateValidators(iter func(id crypto.Addressable, power *big.Int) (stop bool)) (stopped bool) NumValidators() int } @@ -60,8 +60,7 @@ type Tip struct { lastBlockTime time.Time lastBlockHash []byte appHashAfterLastBlock []byte - validators Validators - validatorsWindow ValidatorsWindow + validators *ValidatorsRing } type Blockchain struct { @@ -71,10 +70,13 @@ type Blockchain struct { db dbm.DB } +var _ TipInfo = &Blockchain{} + type PersistedState struct { AppHashAfterLastBlock []byte LastBlockHeight uint64 GenesisDoc genesis.GenesisDoc + Validators PersistedValidatorsRing } func LoadOrNewBlockchain(db dbm.DB, genesisDoc *genesis.GenesisDoc, @@ -103,13 +105,15 @@ func LoadOrNewBlockchain(db dbm.DB, genesisDoc *genesis.GenesisDoc, // Pointer to blockchain state initialised from genesis func newBlockchain(db dbm.DB, genesisDoc *genesis.GenesisDoc) *Blockchain { + vs := NewValidators() + for _, gv := range genesisDoc.Validators { + vs.AlterPower(gv.PublicKey, new(big.Int).SetUint64(gv.Amount)) + } + root := NewRoot(genesisDoc) bc := &Blockchain{ db: db, - Root: NewRoot(genesisDoc), - Tip: NewTip(genesisDoc.ChainID(), NewRoot(genesisDoc).genesisDoc.GenesisTime, NewRoot(genesisDoc).genesisHash), - } - for _, gv := range genesisDoc.Validators { - bc.validators.AlterPower(gv.PublicKey, gv.Amount) + Root: root, + Tip: NewTip(genesisDoc.ChainID(), root.genesisDoc.GenesisTime, root.genesisHash, vs), } return bc } @@ -119,13 +123,11 @@ func loadBlockchain(db dbm.DB) (*Blockchain, error) { if len(buf) == 0 { return nil, nil } - persistedState, err := Decode(buf) + bc, err := DecodeBlockchain(buf) if err != nil { return nil, err } - bc := newBlockchain(db, &persistedState.GenesisDoc) - bc.lastBlockHeight = persistedState.LastBlockHeight - bc.appHashAfterLastBlock = persistedState.AppHashAfterLastBlock + bc.db = db return bc, nil } @@ -137,16 +139,19 @@ func NewRoot(genesisDoc *genesis.GenesisDoc) *Root { } // Create genesis Tip -func NewTip(chainID string, genesisTime time.Time, genesisHash []byte) *Tip { +func NewTip(chainID string, genesisTime time.Time, genesisHash []byte, initialValidators *Validators) *Tip { return &Tip{ chainID: chainID, lastBlockTime: genesisTime, appHashAfterLastBlock: genesisHash, - validators: NewValidators(), - validatorsWindow: NewValidatorsWindow(DefaultValidatorsWindowSize), + validators: NewValidatorsRing(initialValidators, DefaultValidatorsWindowSize), } } +func (bc *Blockchain) AlterPower(id crypto.Addressable, power *big.Int) (*big.Int, error) { + return bc.validators.AlterPower(id, power) +} + func (bc *Blockchain) CommitBlock(blockTime time.Time, blockHash, appHash []byte) error { bc.Lock() defer bc.Unlock() @@ -157,6 +162,14 @@ func (bc *Blockchain) CommitBlock(blockTime time.Time, blockHash, appHash []byte if err != nil { return err } + maxFlow := bc.validators.MaxFlow() + // Update validator set + _, totalFlow := bc.validators.Rotate() + if totalFlow.Cmp(maxFlow) == 1 { + return fmt.Errorf("total flow during block was %v exceeding the maximum allowablw flow of %v - this "+ + "should have been prevented on a per transaction basis - we cannot continue", + totalFlow, maxFlow) + } bc.lastBlockHeight += 1 bc.lastBlockTime = blockTime bc.lastBlockHash = blockHash @@ -175,26 +188,33 @@ func (bc *Blockchain) save() error { return nil } +var cdc = amino.NewCodec() + func (bc *Blockchain) Encode() ([]byte, error) { persistedState := &PersistedState{ GenesisDoc: bc.genesisDoc, AppHashAfterLastBlock: bc.appHashAfterLastBlock, LastBlockHeight: bc.lastBlockHeight, + Validators: bc.validators.Persistable(), } - encodedState, err := json.Marshal(persistedState) + encodedState, err := cdc.MarshalBinary(persistedState) if err != nil { return nil, err } return encodedState, nil } -func Decode(encodedState []byte) (*PersistedState, error) { +func DecodeBlockchain(encodedState []byte) (*Blockchain, error) { persistedState := new(PersistedState) - err := json.Unmarshal(encodedState, persistedState) + err := cdc.UnmarshalBinary(encodedState, persistedState) if err != nil { return nil, err } - return persistedState, nil + bc := newBlockchain(nil, &persistedState.GenesisDoc) + bc.lastBlockHeight = persistedState.LastBlockHeight + bc.appHashAfterLastBlock = persistedState.AppHashAfterLastBlock + bc.validators = UnpersistValidatorsRing(persistedState.Validators) + return bc, nil } func (r *Root) GenesisHash() []byte { @@ -225,10 +245,14 @@ func (t *Tip) AppHashAfterLastBlock() []byte { return t.appHashAfterLastBlock } -func (t *Tip) IterateValidators(iter func(publicKey crypto.PublicKey, power uint64) (stop bool)) (stopped bool) { - return t.validators.Iterate(iter) +func (t *Tip) Validators() *Validators { + return t.validators.Prev().Copy() +} + +func (t *Tip) IterateValidators(iter func(id crypto.Addressable, power *big.Int) (stop bool)) (stopped bool) { + return t.validators.Prev().Copy().Iterate(iter) } func (t *Tip) NumValidators() int { - return t.validators.Length() + return t.validators.Prev().Count() } diff --git a/blockchain/blockchain_test.go b/blockchain/blockchain_test.go new file mode 100644 index 00000000..53d74be3 --- /dev/null +++ b/blockchain/blockchain_test.go @@ -0,0 +1,46 @@ +package blockchain + +import ( + "fmt" + "math/big" + "testing" + "time" + + "github.com/hyperledger/burrow/genesis" + "github.com/hyperledger/burrow/logging/config" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tmlibs/db" +) + +func TestBlockchain_Encode(t *testing.T) { + genesisDoc, _, validators := genesis.NewDeterministicGenesis(234). + GenesisDoc(5, true, 232, 3, true, 34) + bc := newBlockchain(db.NewMemDB(), genesisDoc) + bs, err := bc.Encode() + require.NoError(t, err) + bcOut, err := DecodeBlockchain(bs) + require.True(t, bc.validators.Equal(bcOut.validators)) + require.Equal(t, bc.genesisDoc.GenesisTime, bcOut.genesisDoc.GenesisTime) + assert.Equal(t, config.JSONString(bc.genesisDoc), config.JSONString(bcOut.genesisDoc)) + require.Equal(t, bc.genesisDoc.Hash(), bcOut.genesisDoc.Hash()) + power := new(big.Int).SetUint64(genesisDoc.Validators[1].Amount) + id1 := validators[1].PublicKey() + var flow *big.Int + for i := 0; i < 100; i++ { + power := power.Div(power, big.NewInt(2)) + flow, err = bc.AlterPower(id1, power) + fmt.Println(flow) + require.NoError(t, err) + err = bc.CommitBlock(time.Now(), []byte("blockhash"), []byte("apphash")) + require.NoError(t, err) + bs, err = bc.Encode() + require.NoError(t, err) + bcOut, err = DecodeBlockchain(bs) + require.True(t, bc.validators.Equal(bcOut.validators)) + } + + // Should have exponentially decayed to 0 + assertZero(t, flow) + assertZero(t, bc.validators.Prev().Power(id1)) +} diff --git a/blockchain/validators.go b/blockchain/validators.go index 0ba398af..ea2ddfd4 100644 --- a/blockchain/validators.go +++ b/blockchain/validators.go @@ -2,121 +2,174 @@ package blockchain import ( "fmt" - + "math/big" "sort" + "strings" - "bytes" - "encoding/binary" - - burrowBinary "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/crypto" ) -// A Validator multiset +var big0 = big.NewInt(0) + +// A Validator multiset - can be used to capture the global state of validators or as an accumulator each block type Validators struct { - power map[crypto.Address]uint64 - publicKey map[crypto.Address]crypto.PublicKey - totalPower uint64 + powers map[crypto.Address]*big.Int + publicKeys map[crypto.Address]crypto.Addressable + totalPower *big.Int } -func NewValidators() Validators { - return Validators{ - power: make(map[crypto.Address]uint64), - publicKey: make(map[crypto.Address]crypto.PublicKey), +type ValidatorSet interface { + AlterPower(id crypto.Addressable, power *big.Int) (flow *big.Int, err error) +} + +// Create a new Validators which can act as an accumulator for validator power changes +func NewValidators() *Validators { + return &Validators{ + totalPower: new(big.Int), + powers: make(map[crypto.Address]*big.Int), + publicKeys: make(map[crypto.Address]crypto.Addressable), } } -// Add the power of a validator -func (vs *Validators) AlterPower(publicKey crypto.PublicKey, power uint64) error { - address := publicKey.Address() - // Remove existing power (possibly 0) from total - vs.totalPower -= vs.power[address] - if burrowBinary.IsUint64SumOverflow(vs.totalPower, power) { - // Undo removing existing power - vs.totalPower += vs.power[address] - return fmt.Errorf("could not increase total validator power by %v from %v since that would overflow "+ - "uint64", power, vs.totalPower) +// Add the power of a validator and returns the flow into that validator +func (vs *Validators) AlterPower(id crypto.Addressable, power *big.Int) *big.Int { + if power.Sign() == -1 { + panic("ASRRRH") } - vs.publicKey[address] = publicKey - vs.power[address] = power - // Note we are adjusting by the difference in power (+/-) since we subtracted the previous amount above - vs.totalPower += power - return nil -} - -func (vs *Validators) AddPower(publicKey crypto.PublicKey, power uint64) error { - currentPower := vs.power[publicKey.Address()] - if burrowBinary.IsUint64SumOverflow(currentPower, power) { - return fmt.Errorf("could add power %v to validator %v with power %v because that would overflow uint64", - power, publicKey.Address(), currentPower) + address := id.Address() + // Calculcate flow into this validator (postive means in, negative means out) + flow := new(big.Int).Sub(power, vs.Power(id)) + vs.totalPower.Add(vs.totalPower, flow) + if power.Cmp(big0) == 0 { + // Remove from set so that we return an accurate length + delete(vs.publicKeys, address) + delete(vs.powers, address) + return flow } - return vs.AlterPower(publicKey, vs.power[publicKey.Address()]+power) + vs.publicKeys[address] = crypto.MemoizeAddressable(id) + vs.powers[address] = new(big.Int).Set(power) + return flow +} + +// Adds vsOther to vs +func (vs *Validators) Add(vsOther *Validators) { + vsOther.Iterate(func(id crypto.Addressable, power *big.Int) (stop bool) { + vs.AddPower(id, power) + return + }) +} + +func (vs *Validators) AddPower(id crypto.Addressable, power *big.Int) { + // Current power + power + vs.AlterPower(id, new(big.Int).Add(vs.Power(id), power)) } -func (vs *Validators) SubtractPower(publicKey crypto.PublicKey, power uint64) error { - currentPower := vs.power[publicKey.Address()] - if currentPower < power { - return fmt.Errorf("could subtract power %v from validator %v with power %v because that would "+ - "underflow uint64", power, publicKey.Address(), currentPower) +// Subtracts vsOther from vs +func (vs *Validators) Subtract(vsOther *Validators) { + vsOther.Iterate(func(id crypto.Addressable, power *big.Int) (stop bool) { + vs.SubtractPower(id, power) + return + }) +} + +func (vs *Validators) SubtractPower(id crypto.Addressable, power *big.Int) { + // Current power - power + thisPower := vs.Power(id) + vs.AlterPower(id, new(big.Int).Sub(thisPower, power)) +} + +func (vs *Validators) Power(id crypto.Addressable) *big.Int { + if vs.powers[id.Address()] == nil { + return new(big.Int) } - return vs.AlterPower(publicKey, vs.power[publicKey.Address()]-power) + return new(big.Int).Set(vs.powers[id.Address()]) +} + +func (vs *Validators) Equal(vsOther *Validators) bool { + if vs.Count() != vsOther.Count() { + return false + } + // Stop iteration IFF we find a non-matching validator + return !vs.Iterate(func(id crypto.Addressable, power *big.Int) (stop bool) { + otherPower := vsOther.Power(id) + if otherPower.Cmp(power) != 0 { + return true + } + return false + }) } // Iterates over validators sorted by address -func (vs *Validators) Iterate(iter func(publicKey crypto.PublicKey, power uint64) (stop bool)) (stopped bool) { - addresses := make(crypto.Addresses, 0, len(vs.power)) - for address := range vs.power { +func (vs *Validators) Iterate(iter func(id crypto.Addressable, power *big.Int) (stop bool)) (stopped bool) { + if vs == nil { + return + } + addresses := make(crypto.Addresses, 0, len(vs.powers)) + for address := range vs.powers { addresses = append(addresses, address) } sort.Sort(addresses) for _, address := range addresses { - if iter(vs.publicKey[address], vs.power[address]) { + if iter(vs.publicKeys[address], new(big.Int).Set(vs.powers[address])) { return true } } - return false + return } -func (vs *Validators) Length() int { - return len(vs.power) +func (vs *Validators) Count() int { + return len(vs.publicKeys) } -func (vs *Validators) TotalPower() uint64 { - return vs.totalPower +func (vs *Validators) TotalPower() *big.Int { + return new(big.Int).Set(vs.totalPower) } -// Uses the fixed width public key encoding to -func (vs *Validators) Encode() []byte { - buffer := new(bytes.Buffer) - // varint buffer - buf := make([]byte, 8) - vs.Iterate(func(publicKey crypto.PublicKey, power uint64) (stop bool) { - buffer.Write(publicKey.Encode()) - buffer.Write(buf[:binary.PutUvarint(buf, power)]) +func (vs *Validators) Copy() *Validators { + vsCopy := NewValidators() + vs.Iterate(func(id crypto.Addressable, power *big.Int) (stop bool) { + vsCopy.AlterPower(id, power) return }) - return buffer.Bytes() + return vsCopy } -// Decodes validators encoded with Encode - expects the exact encoded size with no trailing bytes -func DecodeValidators(encoded []byte, validators *Validators) error { - publicKey := new(crypto.PublicKey) - i := 0 - for i < len(encoded) { - n, err := crypto.DecodePublicKeyFixedWidth(encoded[i:], publicKey) - if err != nil { - return err - } - i += n - power, n := binary.Uvarint(encoded[i:]) - if n <= 0 { - return fmt.Errorf("error decoding uint64 from validators binary encoding") - } - i += n - err = validators.AlterPower(*publicKey, power) - if err != nil { - return err - } +type PersistedValidator struct { + PublicKey crypto.PublicKey + PowerBytes []byte +} + +func (vs *Validators) Persistable() []PersistedValidator { + if vs == nil { + return nil + } + pvs := make([]PersistedValidator, 0, vs.Count()) + vs.Iterate(func(id crypto.Addressable, power *big.Int) (stop bool) { + pvs = append(pvs, PersistedValidator{PublicKey: id.PublicKey(), PowerBytes: power.Bytes()}) + return + }) + return pvs +} + +func UnpersistValidators(pvs []PersistedValidator) *Validators { + vs := NewValidators() + for _, pv := range pvs { + power := new(big.Int).SetBytes(pv.PowerBytes) + vs.AlterPower(pv.PublicKey, power) } - return nil + return vs +} + +func (vs *Validators) String() string { + return fmt.Sprintf("Validators{TotalPower: %v; Count: %v; %v}", vs.TotalPower(), vs.Count(), + vs.ValidatorStrings()) +} + +func (vs *Validators) ValidatorStrings() string { + strs := make([]string, 0, vs.Count()) + vs.Iterate(func(id crypto.Addressable, power *big.Int) (stop bool) { + strs = append(strs, fmt.Sprintf("%v->%v", id.Address(), power)) + return + }) + return strings.Join(strs, ", ") } diff --git a/blockchain/validators_ring.go b/blockchain/validators_ring.go new file mode 100644 index 00000000..2709b27c --- /dev/null +++ b/blockchain/validators_ring.go @@ -0,0 +1,198 @@ +package blockchain + +import ( + "fmt" + "math/big" + + "github.com/hyperledger/burrow/crypto" +) + +type ValidatorsRing struct { + buckets []*Validators + // Totals for each validator across all buckets + power *Validators + // Current flow totals for each validator in the Head bucket + flow *Validators + // Index of current head bucekt + head int64 + size int64 +} + +var big1 = big.NewInt(1) +var big3 = big.NewInt(3) + +// Provides a sliding window over the last size buckets of validator power changes +func NewValidatorsRing(initialValidators *Validators, size int) *ValidatorsRing { + if size < 2 { + size = 2 + } + vw := &ValidatorsRing{ + buckets: make([]*Validators, size), + power: NewValidators(), + flow: NewValidators(), + size: int64(size), + } + for i := 0; i < size; i++ { + vw.buckets[i] = NewValidators() + } + + initialValidators.Iterate(func(id crypto.Addressable, power *big.Int) (stop bool) { + // Existing set + vw.buckets[vw.index(-1)].AlterPower(id, power) + // Current accumulator + vw.buckets[vw.head].AlterPower(id, power) + + vw.power.AddPower(id, power.Add(power, power)) + return + }) + + return vw +} + +// Updates the current head bucket (accumulator) with some safety checks +func (vw *ValidatorsRing) AlterPower(id crypto.Addressable, power *big.Int) (*big.Int, error) { + if power.Sign() == -1 { + return nil, fmt.Errorf("cannot set negative validator power: %v", power) + } + // if flow > maxflow then we cannot alter the power + flow := vw.Flow(id, power) + maxFlow := vw.MaxFlow() + // Set flow to update total flow + vw.flow.AlterPower(id, flow) + if vw.flow.totalPower.Cmp(maxFlow) == 1 { + // Reset flow to previous value + vw.flow.AlterPower(id, vw.Flow(id, vw.Head().Power(id))) + allowable := new(big.Int).Sub(maxFlow, vw.flow.totalPower) + return nil, fmt.Errorf("cannot change validator power of %v from %v to %v because that would result in a flow "+ + "greater than or equal to 1/3 of total power for the next commit: flow induced by change: %v, "+ + "current total flow: %v/%v (cumulative/max), remaining allowable flow: %v", + id.Address(), vw.Prev().Power(id), power, flow, vw.flow.totalPower, maxFlow, allowable) + } + // Add to total power + vw.Head().AlterPower(id, power) + return flow, nil +} + +// Returns the flow that would be induced by a validator change by comparing the current accumulator with the previous +// bucket +func (vw *ValidatorsRing) Flow(id crypto.Addressable, power *big.Int) *big.Int { + flow := new(big.Int) + prevPower := vw.Prev().Power(id) + return flow.Abs(flow.Sub(power, prevPower)) +} + +// To ensure that in the maximum valildator shift at least one unit +// of validator power in the intersection of last block validators and this block validators must have at least one +// non-byzantine validator who can tell you if you've been lied to about the validator set +// So need at most ceiling((Total Power)/3) - 1, in integer division we have ceiling(X*p/q) = (p(X+1)-1)/q +// For p = 1 just X/q +// So we want (Total Power)/3 - 1 +func (vw *ValidatorsRing) MaxFlow() *big.Int { + max := vw.Prev().TotalPower() + return max.Sub(max.Div(max, big3), big1) +} + +// Advance the current head bucket to the next bucket and returns the change in total power between the previous bucket +// and the current head, and the total flow which is the sum of absolute values of all changes each validator's power +// after rotation the next head is a copy of the current head +func (vw *ValidatorsRing) Rotate() (totalPowerChange *big.Int, totalFlow *big.Int) { + // Subtract the previous bucket total power so we can add on the current buckets power after this + totalPowerChange = new(big.Int).Sub(vw.Head().totalPower, vw.Prev().totalPower) + // Capture flow before we wipe it + totalFlow = vw.flow.totalPower + // Subtract the tail bucket (if any) from the total + vw.power.Subtract(vw.Next()) + // Copy head bucket + headCopy := vw.Head().Copy() + // add it to total + vw.power.Add(headCopy) + // move the ring buffer on + vw.head = vw.index(1) + // Overwrite new head bucket (previous tail) with previous bucket copy updated with current head + vw.buckets[vw.head] = headCopy + // New flow accumulator1 + vw.flow = NewValidators() + // Advance the ring + return totalPowerChange, totalFlow +} + +func (vw *ValidatorsRing) Prev() *Validators { + return vw.buckets[vw.index(-1)] +} + +func (vw *ValidatorsRing) Head() *Validators { + return vw.buckets[vw.head] +} + +func (vw *ValidatorsRing) Next() *Validators { + return vw.buckets[vw.index(1)] +} + +func (vw *ValidatorsRing) index(i int64) int64 { + return (vw.size + vw.head + i) % vw.size +} + +func (vw *ValidatorsRing) Size() int64 { + return vw.size +} + +// Returns buckets in order head, previous, ... +func (vw *ValidatorsRing) OrderedBuckets() []*Validators { + buckets := make([]*Validators, len(vw.buckets)) + for i := int64(0); i < vw.size; i++ { + buckets[i] = vw.buckets[vw.index(-i)] + } + return buckets +} + +func (vw *ValidatorsRing) String() string { + return fmt.Sprintf("ValidatorsWindow{Total: %v; Buckets: Head->%v<-Tail}", vw.power, vw.OrderedBuckets()) +} + +func (vw *ValidatorsRing) Equal(vwOther *ValidatorsRing) bool { + if vw.size != vwOther.size || vw.head != vwOther.head || len(vw.buckets) != len(vwOther.buckets) || + !vw.flow.Equal(vwOther.flow) || !vw.power.Equal(vwOther.power) { + return false + } + for i, b := range vw.buckets { + if !b.Equal(vwOther.buckets[i]) { + return false + } + } + return true +} + +type PersistedValidatorsRing struct { + Buckets [][]PersistedValidator + Power []PersistedValidator + Flow []PersistedValidator + Head int64 +} + +func (vw *ValidatorsRing) Persistable() PersistedValidatorsRing { + buckets := make([][]PersistedValidator, len(vw.buckets)) + for i, vs := range vw.buckets { + buckets[i] = vs.Persistable() + } + return PersistedValidatorsRing{ + Buckets: buckets, + Power: vw.power.Persistable(), + Flow: vw.flow.Persistable(), + Head: vw.head, + } +} + +func UnpersistValidatorsRing(pvr PersistedValidatorsRing) *ValidatorsRing { + buckets := make([]*Validators, len(pvr.Buckets)) + for i, pv := range pvr.Buckets { + buckets[i] = UnpersistValidators(pv) + } + + return &ValidatorsRing{ + buckets: buckets, + head: pvr.Head, + power: UnpersistValidators(pvr.Power), + flow: UnpersistValidators(pvr.Flow), + size: int64(len(buckets)), + } +} diff --git a/blockchain/validators_ring_test.go b/blockchain/validators_ring_test.go new file mode 100644 index 00000000..d0c81628 --- /dev/null +++ b/blockchain/validators_ring_test.go @@ -0,0 +1,121 @@ +package blockchain + +import ( + "fmt" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var pubA = pubKey(1) +var pubB = pubKey(2) +var pubC = pubKey(3) + +func TestValidatorsWindow_AlterPower(t *testing.T) { + vs := NewValidators() + powAInitial := int64(10000) + vs.AlterPower(pubA, big.NewInt(powAInitial)) + vw := NewValidatorsRing(vs, 3) + + // Just allowable validator tide + var powA, powB, powC int64 = 7000, 23, 309 + powerChange, totalFlow, err := alterPowers(t, vw, powA, powB, powC) + require.NoError(t, err) + assert.Equal(t, big.NewInt(powA+powB+powC-powAInitial), powerChange) + assert.Equal(t, big.NewInt(powAInitial/3-1), totalFlow) + + // This one is not + vw = NewValidatorsRing(vs, 5) + powA, powB, powC = 7000, 23, 310 + powerChange, totalFlow, err = alterPowers(t, vw, powA, powB, powC) + require.Error(t, err) + + powA, powB, powC = 7000, 23, 309 + powerChange, totalFlow, err = alterPowers(t, vw, powA, powB, powC) + require.NoError(t, err) + assert.Equal(t, big.NewInt(powA+powB+powC-powAInitial), powerChange) + assert.Equal(t, big.NewInt(powAInitial/3-1), totalFlow) + + powA, powB, powC = 7000, 23, 309 + powerChange, totalFlow, err = alterPowers(t, vw, powA, powB, powC) + require.NoError(t, err) + assertZero(t, powerChange) + assertZero(t, totalFlow) + + _, err = vw.AlterPower(pubA, big.NewInt(8000)) + assert.NoError(t, err) + + // Should fail - not enough flow left + _, err = vw.AlterPower(pubB, big.NewInt(2000)) + assert.Error(t, err) + + // Take a bit off shouhd work + _, err = vw.AlterPower(pubA, big.NewInt(7000)) + assert.NoError(t, err) + + _, err = vw.AlterPower(pubB, big.NewInt(2000)) + assert.NoError(t, err) + vw.Rotate() + + powerChange, totalFlow, err = alterPowers(t, vw, powA, powB, powC) + require.NoError(t, err) + assert.Equal(t, big.NewInt(-1977), powerChange) + assert.Equal(t, big.NewInt(1977), totalFlow) + + powerChange, totalFlow, err = alterPowers(t, vw, powA, powB, powC) + require.NoError(t, err) + assertZero(t, powerChange) + assert.Equal(t, big0, totalFlow) + + powerChange, totalFlow, err = alterPowers(t, vw, powA, powB, powC) + require.NoError(t, err) + assertZero(t, powerChange) + assert.Equal(t, big0, totalFlow) +} + +func TestValidatorsRing_Persistable(t *testing.T) { + + vs := NewValidators() + powAInitial := int64(10000) + vs.AlterPower(pubA, big.NewInt(powAInitial)) + vw := NewValidatorsRing(vs, 30) + + for i := int64(0); i < 61; i++ { + _, _, err := alterPowers(t, vw, 10000, 200*i, 200*((i+1)%4)) + require.NoError(t, err) + } + + vwOut := UnpersistValidatorsRing(vw.Persistable()) + assert.True(t, vw.Equal(vwOut), "should re equal across persistence") +} + +func alterPowers(t testing.TB, vw *ValidatorsRing, powA, powB, powC int64) (powerChange, totalFlow *big.Int, err error) { + fmt.Println(vw) + _, err = vw.AlterPower(pubA, big.NewInt(powA)) + if err != nil { + return nil, nil, err + } + _, err = vw.AlterPower(pubB, big.NewInt(powB)) + if err != nil { + return nil, nil, err + } + _, err = vw.AlterPower(pubC, big.NewInt(powC)) + if err != nil { + return nil, nil, err + } + maxFlow := vw.MaxFlow() + powerChange, totalFlow = vw.Rotate() + // totalFlow > maxFlow + if totalFlow.Cmp(maxFlow) == 1 { + return powerChange, totalFlow, fmt.Errorf("totalFlow (%v) exceeds maxFlow (%v)", totalFlow, maxFlow) + } + + return powerChange, totalFlow, nil +} + +// Since we have -0 and 0 with big.Int due to its representation with a neg flag +func assertZero(t testing.TB, i *big.Int) { + assert.True(t, big0.Cmp(i) == 0, "expected 0 but got %v", i) +} diff --git a/blockchain/validators_test.go b/blockchain/validators_test.go index 433db89e..1bc7970e 100644 --- a/blockchain/validators_test.go +++ b/blockchain/validators_test.go @@ -1,48 +1,23 @@ package blockchain import ( - "testing" - "fmt" - - "math/rand" + "math/big" + "testing" "github.com/hyperledger/burrow/acm" "github.com/hyperledger/burrow/crypto" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestValidators_AlterPower(t *testing.T) { vs := NewValidators() - pow1 := uint64(2312312321) - assert.NoError(t, vs.AlterPower(pubKey(1), pow1)) + pow1 := big.NewInt(2312312321) + pubA := pubKey(1) + vs.AlterPower(pubA, pow1) assert.Equal(t, pow1, vs.TotalPower()) -} - -func TestValidators_Encode(t *testing.T) { - vs := NewValidators() - rnd := rand.New(rand.NewSource(43534543)) - for i := 0; i < 100; i++ { - power := uint64(rnd.Intn(10)) - require.NoError(t, vs.AlterPower(pubKey(rnd.Int63()), power)) - } - encoded := vs.Encode() - vsOut := NewValidators() - require.NoError(t, DecodeValidators(encoded, &vsOut)) - // Check decoded matches encoded - var publicKeyPower []interface{} - vs.Iterate(func(publicKey crypto.PublicKey, power uint64) (stop bool) { - publicKeyPower = append(publicKeyPower, publicKey, power) - return - }) - vsOut.Iterate(func(publicKey crypto.PublicKey, power uint64) (stop bool) { - assert.Equal(t, publicKeyPower[0], publicKey) - assert.Equal(t, publicKeyPower[1], power) - publicKeyPower = publicKeyPower[2:] - return - }) - assert.Len(t, publicKeyPower, 0, "should exhaust all validators in decoded multiset") + vs.AlterPower(pubA, big.NewInt(0)) + assertZero(t, vs.TotalPower()) } func pubKey(secret interface{}) crypto.PublicKey { diff --git a/blockchain/validators_window.go b/blockchain/validators_window.go deleted file mode 100644 index 11ebf209..00000000 --- a/blockchain/validators_window.go +++ /dev/null @@ -1,64 +0,0 @@ -package blockchain - -import ( - "github.com/hyperledger/burrow/crypto" -) - -type ValidatorsWindow struct { - Buckets []Validators - Total Validators - head int -} - -// Provides a sliding window over the last size buckets of validator power changes -func NewValidatorsWindow(size int) ValidatorsWindow { - if size < 1 { - size = 1 - } - vw := ValidatorsWindow{ - Buckets: make([]Validators, size), - Total: NewValidators(), - } - vw.Buckets[vw.head] = NewValidators() - return vw -} - -// Updates the current head bucket (accumulator) -func (vw *ValidatorsWindow) AlterPower(publicKey crypto.PublicKey, power uint64) error { - return vw.Buckets[vw.head].AlterPower(publicKey, power) -} - -func (vw *ValidatorsWindow) CommitInto(validatorsToUpdate *Validators) error { - var err error - if vw.Buckets[vw.head].Iterate(func(publicKey crypto.PublicKey, power uint64) (stop bool) { - // Update the sink validators - err = validatorsToUpdate.AlterPower(publicKey, power) - if err != nil { - return true - } - // Add to total power - err = vw.Total.AddPower(publicKey, power) - if err != nil { - return true - } - return false - }) { - // If iteration stopped there was an error - return err - } - // move the ring buffer on - vw.head = (vw.head + 1) % len(vw.Buckets) - // Subtract the tail bucket (if any) from the total - if vw.Buckets[vw.head].Iterate(func(publicKey crypto.PublicKey, power uint64) (stop bool) { - err = vw.Total.SubtractPower(publicKey, power) - if err != nil { - return false - } - return true - }) { - return err - } - // Clear new head bucket (and possibly previous tail) - vw.Buckets[vw.head] = NewValidators() - return nil -} diff --git a/cmd/burrow/commands/configure.go b/cmd/burrow/commands/configure.go index c64d4c72..d45ed4d3 100644 --- a/cmd/burrow/commands/configure.go +++ b/cmd/burrow/commands/configure.go @@ -155,7 +155,7 @@ func Configure(output Output) func(cmd *cli.Cmd) { if nodeKey { privKey := tm_crypto.GenPrivKeyEd25519() - copy(privKey[:], key.PrivateKey.PrivateKey) + copy(privKey[:], key.PrivateKey.Key) nodeKey := &p2p.NodeKey{ PrivKey: privKey, } diff --git a/consensus/tendermint/abci/app.go b/consensus/tendermint/abci/app.go index 6c524748..41aed6b4 100644 --- a/consensus/tendermint/abci/app.go +++ b/consensus/tendermint/abci/app.go @@ -2,6 +2,7 @@ package abci import ( "fmt" + "math/big" "sync" "time" @@ -82,15 +83,57 @@ func (app *App) Query(reqQuery abciTypes.RequestQuery) (respQuery abciTypes.Resp } func (app *App) InitChain(chain abciTypes.RequestInitChain) (respInitChain abciTypes.ResponseInitChain) { - // Could verify agreement on initial validator set here + defer func() { + if r := recover(); r != nil { + app.panicFunc(fmt.Errorf("panic occurred in abci.App/InitChain: %v\n%s", r, debug.Stack())) + } + }() + err := app.checkValidatorsMatch(chain.Validators) + if err != nil { + app.logger.InfoMsg("Initial validator set mistmatch", structure.ErrorKey, err) + panic(err) + } + app.logger.InfoMsg("Initial validator set matches") return } func (app *App) BeginBlock(block abciTypes.RequestBeginBlock) (respBeginBlock abciTypes.ResponseBeginBlock) { app.block = &block + defer func() { + if r := recover(); r != nil { + app.panicFunc(fmt.Errorf("panic occurred in abci.App/BeginBlock: %v\n%s", r, debug.Stack())) + } + }() + if block.Header.Height > 1 { + validators := make([]abciTypes.Validator, len(block.Validators)) + for i, v := range block.Validators { + validators[i] = v.Validator + } + err := app.checkValidatorsMatch(validators) + if err != nil { + panic(err) + } + } return } +func (app *App) checkValidatorsMatch(validators []abciTypes.Validator) error { + tendermintValidators := bcm.NewValidators() + for _, v := range validators { + publicKey, err := crypto.PublicKeyFromABCIPubKey(v.PubKey) + if err != nil { + panic(err) + } + tendermintValidators.AlterPower(publicKey, big.NewInt(v.Power)) + } + burrowValidators := app.blockchain.Validators() + if !burrowValidators.Equal(tendermintValidators) { + return fmt.Errorf("validators provided by Tendermint at InitChain do not match those held by Burrow: "+ + "Tendermint gives: %v, Burrow gives: %v", tendermintValidators, burrowValidators) + } + return nil +} + func (app *App) CheckTx(txBytes []byte) abciTypes.ResponseCheckTx { defer func() { if r := recover(); r != nil { @@ -163,13 +206,13 @@ func txExecutor(executor execution.BatchExecutor, txDecoder txs.Decoder, logger } func (app *App) EndBlock(reqEndBlock abciTypes.RequestEndBlock) abciTypes.ResponseEndBlock { - // Validator mutation goes here var validatorUpdates abciTypes.Validators - app.blockchain.IterateValidators(func(publicKey crypto.PublicKey, power uint64) (stop bool) { + app.blockchain.IterateValidators(func(id crypto.Addressable, power *big.Int) (stop bool) { validatorUpdates = append(validatorUpdates, abciTypes.Validator{ - Address: publicKey.Address().Bytes(), - PubKey: publicKey.ABCIPubKey(), - Power: int64(power), + Address: id.Address().Bytes(), + PubKey: id.PublicKey().ABCIPubKey(), + // Must be ensured during execution + Power: power.Int64(), }) return }) @@ -184,13 +227,14 @@ func (app *App) Commit() abciTypes.ResponseCommit { app.panicFunc(fmt.Errorf("panic occurred in abci.App/Commit: %v\n%s", r, debug.Stack())) } }() + blockTime := time.Unix(app.block.Header.Time, 0) app.logger.InfoMsg("Committing block", "tag", "Commit", structure.ScopeKey, "Commit()", "height", app.block.Header.Height, "hash", app.block.Hash, "txs", app.block.Header.NumTxs, - "block_time", app.block.Header.Time, // [CSK] this sends a fairly non-sensical number; should be human readable + "block_time", blockTime, "last_block_time", app.blockchain.Tip.LastBlockTime(), "last_block_hash", app.blockchain.Tip.LastBlockHash()) @@ -219,37 +263,16 @@ func (app *App) Commit() abciTypes.ResponseCommit { } }() - // First commit the app start, this app hash will not get checkpointed until the next block when we are sure - // that nothing in the downstream commit process could have failed. At worst we go back one block. - blockHeader := app.block.Header - appHash, err := app.committer.Commit(&blockHeader) + appHash, err := app.committer.Commit(app.block.Hash, blockTime, &app.block.Header) if err != nil { panic(errors.Wrap(err, "Could not commit transactions in block to execution state")) } - // Commit to our blockchain state which will checkpoint the previous app hash by saving it to the database - // (we know the previous app hash is safely committed because we are about to commit the next) - err = app.blockchain.CommitBlock(time.Unix(int64(app.block.Header.Time), 0), app.block.Hash, appHash) - if err != nil { - panic(errors.Wrap(err, "could not commit block to blockchain state")) - } - err = app.checker.Reset() if err != nil { panic(errors.Wrap(err, "could not reset check cache during commit")) } - // Perform a sanity check our block height - if app.blockchain.LastBlockHeight() != uint64(app.block.Header.Height) { - app.logger.InfoMsg("Burrow block height disagrees with Tendermint block height", - structure.ScopeKey, "Commit()", - "burrow_height", app.blockchain.LastBlockHeight(), - "tendermint_height", app.block.Header.Height) - - panic(fmt.Errorf("burrow has recorded a block height of %v, "+ - "but Tendermint reports a block height of %v, and the two should agree", - app.blockchain.LastBlockHeight(), app.block.Header.Height)) - } return abciTypes.ResponseCommit{ Data: appHash, } diff --git a/consensus/tendermint/validator/priv_validator_memory.go b/consensus/tendermint/validator/priv_validator_memory.go index 05455cba..3a5b6db3 100644 --- a/consensus/tendermint/validator/priv_validator_memory.go +++ b/consensus/tendermint/validator/priv_validator_memory.go @@ -1,14 +1,13 @@ package validator import ( - "github.com/hyperledger/burrow/acm" "github.com/hyperledger/burrow/crypto" tm_crypto "github.com/tendermint/go-crypto" tm_types "github.com/tendermint/tendermint/types" ) type privValidatorMemory struct { - acm.Addressable + crypto.Addressable signer goCryptoSigner lastSignedInfo *LastSignedInfo } @@ -17,7 +16,7 @@ var _ tm_types.PrivValidator = &privValidatorMemory{} // Create a PrivValidator with in-memory state that takes an addressable representing the validator identity // and a signer providing private signing for that identity. -func NewPrivValidatorMemory(addressable acm.Addressable, signer crypto.Signer) *privValidatorMemory { +func NewPrivValidatorMemory(addressable crypto.Addressable, signer crypto.Signer) *privValidatorMemory { return &privValidatorMemory{ Addressable: addressable, signer: asTendermintSigner(signer), diff --git a/core/kernel.go b/core/kernel.go index fee805da..ab44c026 100644 --- a/core/kernel.go +++ b/core/kernel.go @@ -102,10 +102,10 @@ func NewKernel(ctx context.Context, keyClient keys.KeyClient, privValidator tmTy txCodec := txs.NewAminoCodec() tmGenesisDoc := tendermint.DeriveGenesisDoc(genesisDoc) - checker := execution.NewBatchChecker(state, blockchain.Tip, logger) + checker := execution.NewBatchChecker(state, blockchain, logger) emitter := event.NewEmitter(logger) - committer := execution.NewBatchCommitter(state, blockchain.Tip, emitter, logger, exeOptions...) + committer := execution.NewBatchCommitter(state, blockchain, emitter, logger, exeOptions...) tmNode, err := tendermint.NewNode(tmConf, privValidator, tmGenesisDoc, blockchain, checker, committer, txCodec, kern.Panic, tmLogger) if err != nil { @@ -204,7 +204,6 @@ func NewKernel(ctx context.Context, keyClient keys.KeyClient, privValidator tmTy if err != nil { return nil, err } - listen.Addr() grpcServer := rpc.NewGRPCServer(logger) var ks *keys.KeyStore diff --git a/crypto/address.go b/crypto/address.go index 9dab2015..69bd9af6 100644 --- a/crypto/address.go +++ b/crypto/address.go @@ -10,6 +10,40 @@ import ( "golang.org/x/crypto/ripemd160" ) +type Addressable interface { + // Get the 20 byte EVM address of this account + Address() Address + // Public key from which the Address is derived + PublicKey() PublicKey +} + +func NewAddressable(address Address, publicKey PublicKey) Addressable { + return &memoizedAddressable{ + address: address, + publicKey: publicKey, + } +} + +type memoizedAddressable struct { + publicKey PublicKey + address Address +} + +func MemoizeAddressable(addressable Addressable) Addressable { + if a, ok := addressable.(*memoizedAddressable); ok { + return a + } + return NewAddressable(addressable.Address(), addressable.PublicKey()) +} + +func (a *memoizedAddressable) PublicKey() PublicKey { + return a.publicKey +} + +func (a *memoizedAddressable) Address() Address { + return a.address +} + type Address binary.Word160 type Addresses []Address diff --git a/crypto/crypto.go b/crypto/crypto.go index e5f62db1..eab93dea 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -21,6 +21,16 @@ func (k CurveType) String() string { return "unknown" } } +func (k CurveType) ABCIType() string { + switch k { + case CurveTypeSecp256k1: + return "secp256k1" + case CurveTypeEd25519: + return "ed25519" + default: + return "unknown" + } +} // Get this CurveType's 8 bit identifier as a byte func (k CurveType) Byte() byte { diff --git a/crypto/crypto.pb.go b/crypto/crypto.pb.go index 230f0f5e..2a8e152b 100644 --- a/crypto/crypto.pb.go +++ b/crypto/crypto.pb.go @@ -36,7 +36,7 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package // PublicKey type PublicKey struct { CurveType CurveType `protobuf:"varint,1,opt,name=CurveType,proto3,casttype=CurveType" json:"CurveType,omitempty"` - PublicKey []byte `protobuf:"bytes,2,opt,name=PublicKey,proto3" json:"PublicKey,omitempty"` + Key []byte `protobuf:"bytes,2,opt,name=Key,proto3" json:"Key,omitempty"` } func (m *PublicKey) Reset() { *m = PublicKey{} } @@ -50,9 +50,9 @@ func (m *PublicKey) GetCurveType() CurveType { return 0 } -func (m *PublicKey) GetPublicKey() []byte { +func (m *PublicKey) GetKey() []byte { if m != nil { - return m.PublicKey + return m.Key } return nil } @@ -64,8 +64,8 @@ func (*PublicKey) XXX_MessageName() string { type PrivateKey struct { CurveType CurveType `protobuf:"varint,1,opt,name=CurveType,proto3,casttype=CurveType" json:"CurveType,omitempty"` // Note may need initialisation - PublicKey []byte `protobuf:"bytes,2,opt,name=PublicKey,proto3" json:"PublicKey,omitempty"` - PrivateKey []byte `protobuf:"bytes,3,opt,name=PrivateKey,proto3" json:"PrivateKey,omitempty"` + PublicKey []byte `protobuf:"bytes,2,opt,name=PublicKey,proto3" json:"PublicKey,omitempty"` + Key []byte `protobuf:"bytes,3,opt,name=Key,proto3" json:"Key,omitempty"` } func (m *PrivateKey) Reset() { *m = PrivateKey{} } @@ -101,11 +101,11 @@ func (m *PublicKey) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintCrypto(dAtA, i, uint64(m.CurveType)) } - if len(m.PublicKey) > 0 { + if len(m.Key) > 0 { dAtA[i] = 0x12 i++ - i = encodeVarintCrypto(dAtA, i, uint64(len(m.PublicKey))) - i += copy(dAtA[i:], m.PublicKey) + i = encodeVarintCrypto(dAtA, i, uint64(len(m.Key))) + i += copy(dAtA[i:], m.Key) } return i, nil } @@ -136,11 +136,11 @@ func (m *PrivateKey) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintCrypto(dAtA, i, uint64(len(m.PublicKey))) i += copy(dAtA[i:], m.PublicKey) } - if len(m.PrivateKey) > 0 { + if len(m.Key) > 0 { dAtA[i] = 0x1a i++ - i = encodeVarintCrypto(dAtA, i, uint64(len(m.PrivateKey))) - i += copy(dAtA[i:], m.PrivateKey) + i = encodeVarintCrypto(dAtA, i, uint64(len(m.Key))) + i += copy(dAtA[i:], m.Key) } return i, nil } @@ -160,7 +160,7 @@ func (m *PublicKey) Size() (n int) { if m.CurveType != 0 { n += 1 + sovCrypto(uint64(m.CurveType)) } - l = len(m.PublicKey) + l = len(m.Key) if l > 0 { n += 1 + l + sovCrypto(uint64(l)) } @@ -177,7 +177,7 @@ func (m *PrivateKey) Size() (n int) { if l > 0 { n += 1 + l + sovCrypto(uint64(l)) } - l = len(m.PrivateKey) + l = len(m.Key) if l > 0 { n += 1 + l + sovCrypto(uint64(l)) } @@ -247,7 +247,7 @@ func (m *PublicKey) Unmarshal(dAtA []byte) error { } case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -271,9 +271,9 @@ func (m *PublicKey) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.PublicKey = append(m.PublicKey[:0], dAtA[iNdEx:postIndex]...) - if m.PublicKey == nil { - m.PublicKey = []byte{} + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} } iNdEx = postIndex default: @@ -378,7 +378,7 @@ func (m *PrivateKey) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field PrivateKey", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { @@ -402,9 +402,9 @@ func (m *PrivateKey) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.PrivateKey = append(m.PrivateKey[:0], dAtA[iNdEx:postIndex]...) - if m.PrivateKey == nil { - m.PrivateKey = []byte{} + m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) + if m.Key == nil { + m.Key = []byte{} } iNdEx = postIndex default: @@ -537,19 +537,19 @@ func init() { proto.RegisterFile("crypto.proto", fileDescriptorCrypto) } func init() { golang_proto.RegisterFile("crypto.proto", fileDescriptorCrypto) } var fileDescriptorCrypto = []byte{ - // 221 bytes of a gzipped FileDescriptorProto + // 224 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x49, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x83, 0xf0, 0xa4, 0x74, 0xd3, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0xd3, 0xf3, 0xd3, 0xf3, 0xf5, 0xc1, 0xd2, - 0x49, 0xa5, 0x69, 0x60, 0x1e, 0x98, 0x03, 0x66, 0x41, 0xb4, 0x29, 0xc5, 0x70, 0x71, 0x06, 0x94, + 0x49, 0xa5, 0x69, 0x60, 0x1e, 0x98, 0x03, 0x66, 0x41, 0xb4, 0x29, 0x05, 0x70, 0x71, 0x06, 0x94, 0x26, 0xe5, 0x64, 0x26, 0x7b, 0xa7, 0x56, 0x0a, 0x69, 0x73, 0x71, 0x3a, 0x97, 0x16, 0x95, 0xa5, 0x86, 0x54, 0x16, 0xa4, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x3a, 0xf1, 0xfe, 0xba, 0x27, 0x8f, - 0x10, 0x0c, 0x42, 0x30, 0x85, 0x64, 0x90, 0x74, 0x4a, 0x30, 0x29, 0x30, 0x6a, 0xf0, 0x04, 0x21, - 0x04, 0xac, 0x58, 0x66, 0x2c, 0x90, 0x67, 0x50, 0x6a, 0x64, 0xe4, 0xe2, 0x0a, 0x28, 0xca, 0x2c, - 0x4b, 0x2c, 0x49, 0xa5, 0xae, 0xf9, 0x42, 0x72, 0xc8, 0x06, 0x4b, 0x30, 0x83, 0xa5, 0x91, 0x44, - 0xac, 0x38, 0x3a, 0x16, 0xc8, 0x33, 0x80, 0xdc, 0xe0, 0x64, 0x75, 0xe2, 0x91, 0x1c, 0xe3, 0x85, - 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x1e, 0x78, 0x2c, 0xc7, 0x78, 0xe2, 0xb1, 0x1c, 0x63, - 0x94, 0x0a, 0x52, 0x30, 0x65, 0x54, 0x16, 0xa4, 0x16, 0xe5, 0xa4, 0xa6, 0xa4, 0xa7, 0x16, 0xe9, - 0x27, 0x95, 0x16, 0x15, 0xe5, 0x97, 0xeb, 0x43, 0x02, 0x33, 0x89, 0x0d, 0x1c, 0x48, 0xc6, 0x80, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x5f, 0x49, 0x55, 0xc8, 0x6b, 0x01, 0x00, 0x00, + 0x10, 0x0c, 0x42, 0x30, 0x85, 0x04, 0xb8, 0x98, 0xbd, 0x53, 0x2b, 0x25, 0x98, 0x14, 0x18, 0x35, + 0x78, 0x82, 0x40, 0x4c, 0x2b, 0x96, 0x19, 0x0b, 0xe4, 0x19, 0x94, 0x8a, 0xb9, 0xb8, 0x02, 0x8a, + 0x32, 0xcb, 0x12, 0x4b, 0x52, 0x49, 0x36, 0x52, 0x06, 0xc9, 0x31, 0x50, 0x83, 0x91, 0x5c, 0x07, + 0xb5, 0x90, 0x19, 0x61, 0x21, 0x47, 0xc7, 0x02, 0x79, 0x06, 0x90, 0xa5, 0x4e, 0x56, 0x27, 0x1e, + 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x81, 0xc7, 0x72, 0x8c, 0x27, + 0x1e, 0xcb, 0x31, 0x46, 0xa9, 0x20, 0x85, 0x45, 0x46, 0x65, 0x41, 0x6a, 0x51, 0x4e, 0x6a, 0x4a, + 0x7a, 0x6a, 0x91, 0x7e, 0x52, 0x69, 0x51, 0x51, 0x7e, 0xb9, 0x3e, 0x24, 0xc4, 0x92, 0xd8, 0xc0, + 0x21, 0x61, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x1f, 0x6f, 0x9c, 0xc7, 0x50, 0x01, 0x00, 0x00, } diff --git a/crypto/private_key.go b/crypto/private_key.go index 91252e09..fddafda0 100644 --- a/crypto/private_key.go +++ b/crypto/private_key.go @@ -29,28 +29,28 @@ func PublicKeyFromBytes(bs []byte, curveType CurveType) (PublicKey, error) { return PublicKey{}, ErrInvalidCurve(curveType) } - return PublicKey{PublicKey: bs, CurveType: curveType}, nil + return PublicKey{Key: bs, CurveType: curveType}, nil } func (p PrivateKey) RawBytes() []byte { - return p.PrivateKey + return p.Key } func (p PrivateKey) Sign(msg []byte) (Signature, error) { switch p.CurveType { case CurveTypeEd25519: - if len(p.PrivateKey) != ed25519.PrivateKeySize { + if len(p.Key) != ed25519.PrivateKeySize { return nil, fmt.Errorf("bytes passed have length %v but ed25519 private keys have %v bytes", - len(p.PrivateKey), ed25519.PrivateKeySize) + len(p.Key), ed25519.PrivateKeySize) } - privKey := ed25519.PrivateKey(p.PrivateKey) + privKey := ed25519.PrivateKey(p.Key) return ed25519.Sign(privKey, msg), nil case CurveTypeSecp256k1: - if len(p.PrivateKey) != btcec.PrivKeyBytesLen { + if len(p.Key) != btcec.PrivKeyBytesLen { return nil, fmt.Errorf("bytes passed have length %v but secp256k1 private keys have %v bytes", - len(p.PrivateKey), btcec.PrivKeyBytesLen) + len(p.Key), btcec.PrivKeyBytesLen) } - privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), p.PrivateKey) + privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), p.Key) sig, err := privKey.Sign(msg) if err != nil { @@ -63,7 +63,7 @@ func (p PrivateKey) Sign(msg []byte) (Signature, error) { } func (p PrivateKey) GetPublicKey() PublicKey { - return PublicKey{CurveType: p.CurveType, PublicKey: p.PublicKey} + return PublicKey{CurveType: p.CurveType, Key: p.PublicKey} } // Reinitialise after serialisation @@ -87,7 +87,7 @@ func PrivateKeyFromRawBytes(privKeyBytes []byte, curveType CurveType) (PrivateKe return PrivateKey{}, fmt.Errorf("bytes passed have length %v but ed25519 private keys have %v bytes", len(privKeyBytes), ed25519.PrivateKeySize) } - return PrivateKey{PrivateKey: privKeyBytes, PublicKey: privKeyBytes[32:], CurveType: CurveTypeEd25519}, nil + return PrivateKey{Key: privKeyBytes, PublicKey: privKeyBytes[32:], CurveType: CurveTypeEd25519}, nil case CurveTypeSecp256k1: if len(privKeyBytes) != btcec.PrivKeyBytesLen { return PrivateKey{}, fmt.Errorf("bytes passed have length %v but secp256k1 private keys have %v bytes", @@ -97,7 +97,7 @@ func PrivateKeyFromRawBytes(privKeyBytes []byte, curveType CurveType) (PrivateKe if !bytes.Equal(privKey.Serialize(), privKeyBytes) { return PrivateKey{}, fmt.Errorf("serialisation of Secp256k1 private key bytes does not equal") } - return PrivateKey{PrivateKey: privKeyBytes, PublicKey: pubKey.SerializeCompressed(), CurveType: CurveTypeSecp256k1}, nil + return PrivateKey{Key: privKeyBytes, PublicKey: pubKey.SerializeCompressed(), CurveType: CurveTypeSecp256k1}, nil default: return PrivateKey{}, ErrInvalidCurve(curveType) } diff --git a/crypto/public_key.go b/crypto/public_key.go index f837b38d..2d3da826 100644 --- a/crypto/public_key.go +++ b/crypto/public_key.go @@ -34,7 +34,7 @@ func PublicKeyLength(curveType CurveType) int { func (p PublicKey) MarshalJSON() ([]byte, error) { jStruct := PublicKeyJSON{ CurveType: p.CurveType.String(), - PublicKey: hex.EncodeUpperToString(p.PublicKey), + PublicKey: hex.EncodeUpperToString(p.Key), } txt, err := json.Marshal(jStruct) return txt, err @@ -59,7 +59,7 @@ func (p *PublicKey) UnmarshalJSON(text []byte) error { return err } p.CurveType = CurveType - p.PublicKey = bs + p.Key = bs return nil } @@ -69,15 +69,15 @@ func (p *PublicKey) UnmarshalText(text []byte) error { func (p PublicKey) IsValid() bool { publicKeyLength := PublicKeyLength(p.CurveType) - return publicKeyLength != 0 && publicKeyLength == len(p.PublicKey) + return publicKeyLength != 0 && publicKeyLength == len(p.Key) } func (p PublicKey) Verify(msg []byte, signature Signature) bool { switch p.CurveType { case CurveTypeEd25519: - return ed25519.Verify(p.PublicKey, msg, signature) + return ed25519.Verify(p.Key, msg, signature) case CurveTypeSecp256k1: - pub, err := btcec.ParsePubKey(p.PublicKey, btcec.S256()) + pub, err := btcec.ParsePubKey(p.Key, btcec.S256()) if err != nil { return false } @@ -91,17 +91,21 @@ func (p PublicKey) Verify(msg []byte, signature Signature) bool { } } +func (p PublicKey) PublicKey() PublicKey { + return p +} + func (p PublicKey) Address() Address { switch p.CurveType { case CurveTypeEd25519: // FIMXE: tendermint go-crypto-0.5.0 uses weird scheme, this is fixed in 0.6.0 tmPubKey := new(tmCrypto.PubKeyEd25519) - copy(tmPubKey[:], p.PublicKey) + copy(tmPubKey[:], p.Key) addr, _ := AddressFromBytes(tmPubKey.Address()) return addr case CurveTypeSecp256k1: sha := sha256.New() - sha.Write(p.PublicKey[:]) + sha.Write(p.Key[:]) hash := ripemd160.New() hash.Write(sha.Sum(nil)) @@ -124,29 +128,35 @@ func (p PublicKey) AddressHashType() string { } func (p PublicKey) RawBytes() []byte { - return p.PublicKey[:] + return p.Key[:] } // Return the ABCI PubKey. See Tendermint protobuf.go for the go-crypto conversion this is based on func (p PublicKey) ABCIPubKey() abci.PubKey { - switch p.CurveType { - case CurveTypeEd25519: - return abci.PubKey{ - Type: "ed25519", - Data: p.RawBytes(), - } - case CurveTypeSecp256k1: - return abci.PubKey{ - Type: "secp256k1", - Data: p.RawBytes(), - } - default: - return abci.PubKey{} + return abci.PubKey{ + Type: p.CurveType.ABCIType(), + Data: p.RawBytes(), + } +} + +func PublicKeyFromABCIPubKey(pubKey abci.PubKey) (PublicKey, error) { + switch pubKey.Type { + case CurveTypeEd25519.ABCIType(): + return PublicKey{ + CurveType: CurveTypeEd25519, + Key: pubKey.Data, + }, nil + case CurveTypeSecp256k1.ABCIType(): + return PublicKey{ + CurveType: CurveTypeEd25519, + Key: pubKey.Data, + }, nil } + return PublicKey{}, fmt.Errorf("did not recognise ABCI PubKey type: %s", pubKey.Type) } func (p PublicKey) String() string { - return hex.EncodeUpperToString(p.PublicKey) + return hex.EncodeUpperToString(p.Key) } // Produces a binary encoding of the CurveType byte plus @@ -154,7 +164,7 @@ func (p PublicKey) String() string { func (p PublicKey) Encode() []byte { encoded := make([]byte, PublicKeyLength(p.CurveType)+1) encoded[0] = p.CurveType.Byte() - copy(encoded[1:], p.PublicKey) + copy(encoded[1:], p.Key) return encoded } @@ -174,7 +184,7 @@ func DecodePublicKeyFixedWidth(buf []byte, publicKey *PublicKey) (int, error) { } publicKey.CurveType = curveType - publicKey.PublicKey = buf[1:publicKeyEnd] + publicKey.Key = buf[1:publicKeyEnd] if !publicKey.IsValid() { return publicKeyEnd, fmt.Errorf("decoded public key %v is not valid", publicKey) } diff --git a/execution/executors/call_context.go b/execution/contexts/call_context.go similarity index 99% rename from execution/executors/call_context.go rename to execution/contexts/call_context.go index 0f343819..5278b21c 100644 --- a/execution/executors/call_context.go +++ b/execution/contexts/call_context.go @@ -1,4 +1,4 @@ -package executors +package contexts import ( "fmt" diff --git a/execution/contexts/governance_context.go b/execution/contexts/governance_context.go new file mode 100644 index 00000000..dd0a8afe --- /dev/null +++ b/execution/contexts/governance_context.go @@ -0,0 +1,130 @@ +package contexts + +import ( + "fmt" + "math/big" + + "github.com/hyperledger/burrow/acm" + "github.com/hyperledger/burrow/acm/state" + "github.com/hyperledger/burrow/blockchain" + "github.com/hyperledger/burrow/execution/errors" + "github.com/hyperledger/burrow/execution/exec" + "github.com/hyperledger/burrow/genesis/spec" + "github.com/hyperledger/burrow/logging" + "github.com/hyperledger/burrow/permission" + "github.com/hyperledger/burrow/txs/payload" +) + +type GovernanceContext struct { + StateWriter state.ReaderWriter + ValidatorSet blockchain.ValidatorSet + Logger *logging.Logger + tx *payload.GovernanceTx + txe *exec.TxExecution +} + +// GovernanceTx provides a set of TemplateAccounts and GovernanceContext tries to alter the chain state to match the +// specification given +func (ctx *GovernanceContext) Execute(txe *exec.TxExecution) error { + var ok bool + ctx.txe = txe + ctx.tx, ok = txe.Envelope.Tx.Payload.(*payload.GovernanceTx) + if !ok { + return fmt.Errorf("payload must be NameTx, but is: %v", txe.Envelope.Tx.Payload) + } + accounts, err := getInputs(ctx.StateWriter, ctx.tx.Inputs) + if err != nil { + return err + } + + // ensure all inputs have root permissions + if !allHavePermission(ctx.StateWriter, permission.Root, accounts, ctx.Logger) { + return fmt.Errorf("at least one input lacks Root permission needed for GovernanceTx") + } + + for _, i := range ctx.tx.Inputs { + txe.Input(i.Address, nil) + } + + for _, update := range ctx.tx.AccountUpdates { + if update.Address == nil && update.PublicKey == nil { + // We do not want to generate a key + return fmt.Errorf("could not execution GovernanceTx since account template %v contains neither "+ + "address or public key", update) + } + if update.PublicKey != nil { + address := update.PublicKey.Address() + if update.Address != nil && address != *update.Address { + return fmt.Errorf("supplied public key %v whose address %v does not match %v provided by"+ + "GovernanceTx", update.PublicKey, address, update.Address) + } + update.Address = &address + } + if update.PublicKey == nil && update.Power != nil { + // If we are updating power we will need the key + return fmt.Errorf("GovernanceTx must be provided with public key when updating validator power") + } + account, err := state.GetMutableAccount(ctx.StateWriter, *update.Address) + if err != nil { + return err + } + if account == nil { + return fmt.Errorf("account %v not found so cannot update using template %v", update.Address, update) + } + governAccountEvent, err := ctx.updateAccount(account, update) + if err != nil { + txe.GovernAccount(governAccountEvent, errors.AsException(err)) + return err + } + txe.GovernAccount(governAccountEvent, nil) + } + return nil +} + +func (ctx *GovernanceContext) updateAccount(account *acm.MutableAccount, update *spec.TemplateAccount) (ev *exec.GovernAccountEvent, err error) { + ev = &exec.GovernAccountEvent{ + AccountUpdate: update, + } + if update.Amount != nil { + err = account.SetBalance(*update.Amount) + if err != nil { + return + } + } + if update.NodeAddress != nil { + // TODO: can we do something useful if provided with a NodeAddress for an account about to become a validator + // like add it to persistent peers or pre gossip so it gets inbound connections? If so under which circumstances? + } + if update.Power != nil { + if update.PublicKey == nil { + err = fmt.Errorf("updateAccount should have PublicKey by this point but appears not to for "+ + "template account: %v", update) + return + } + power := new(big.Int).SetUint64(*update.Power) + if !power.IsInt64() { + err = fmt.Errorf("power supplied in update to validator power for %v does not fit into int64 and "+ + "so is not supported by Tendermint", update.Address) + } + _, err := ctx.ValidatorSet.AlterPower(*update.PublicKey, power) + if err != nil { + return ev, err + } + } + perms := account.Permissions() + if len(update.Permissions) > 0 { + perms.Base, err = permission.BasePermissionsFromStringList(update.Permissions) + if err != nil { + return + } + } + if len(update.Roles) > 0 { + perms.Roles = update.Roles + } + err = account.SetPermissions(perms) + if err != nil { + return + } + err = ctx.StateWriter.UpdateAccount(account) + return +} diff --git a/execution/executors/name_context.go b/execution/contexts/name_context.go similarity index 99% rename from execution/executors/name_context.go rename to execution/contexts/name_context.go index 3be220f9..b5ba18a8 100644 --- a/execution/executors/name_context.go +++ b/execution/contexts/name_context.go @@ -1,4 +1,4 @@ -package executors +package contexts import ( "fmt" diff --git a/execution/executors/permissions_context.go b/execution/contexts/permissions_context.go similarity index 99% rename from execution/executors/permissions_context.go rename to execution/contexts/permissions_context.go index 11730e13..4572e41c 100644 --- a/execution/executors/permissions_context.go +++ b/execution/contexts/permissions_context.go @@ -1,4 +1,4 @@ -package executors +package contexts import ( "fmt" diff --git a/execution/executors/send_context.go b/execution/contexts/send_context.go similarity index 93% rename from execution/executors/send_context.go rename to execution/contexts/send_context.go index 813a433b..592cb6c4 100644 --- a/execution/executors/send_context.go +++ b/execution/contexts/send_context.go @@ -1,4 +1,4 @@ -package executors +package contexts import ( "fmt" @@ -8,6 +8,7 @@ import ( "github.com/hyperledger/burrow/execution/errors" "github.com/hyperledger/burrow/execution/exec" "github.com/hyperledger/burrow/logging" + "github.com/hyperledger/burrow/permission" "github.com/hyperledger/burrow/txs/payload" ) @@ -30,7 +31,7 @@ func (ctx *SendContext) Execute(txe *exec.TxExecution) error { } // ensure all inputs have send permissions - if !hasSendPermission(ctx.StateWriter, accounts, ctx.Logger) { + if !allHavePermission(ctx.StateWriter, permission.Send, accounts, ctx.Logger) { return fmt.Errorf("at least one input lacks permission for SendTx") } diff --git a/execution/executors/shared.go b/execution/contexts/shared.go similarity index 96% rename from execution/executors/shared.go rename to execution/contexts/shared.go index 33ad90a0..ed97bbd1 100644 --- a/execution/executors/shared.go +++ b/execution/contexts/shared.go @@ -1,4 +1,4 @@ -package executors +package contexts import ( "fmt" @@ -189,10 +189,10 @@ func HasPermission(accountGetter state.AccountGetter, acc acm.Account, perm perm } // TODO: for debug log the failed accounts -func hasSendPermission(accountGetter state.AccountGetter, accs map[crypto.Address]*acm.MutableAccount, - logger *logging.Logger) bool { +func allHavePermission(accountGetter state.AccountGetter, perm permission.PermFlag, + accs map[crypto.Address]*acm.MutableAccount, logger *logging.Logger) bool { for _, acc := range accs { - if !HasPermission(accountGetter, acc, permission.Send, logger) { + if !HasPermission(accountGetter, acc, perm, logger) { return false } } diff --git a/execution/evm/snative.go b/execution/evm/snative.go index 06254915..d10b851d 100644 --- a/execution/evm/snative.go +++ b/execution/evm/snative.go @@ -356,7 +356,7 @@ func hasBase(state state.ReaderWriter, caller acm.Account, args []byte, gas *uin return nil, fmt.Errorf("unknown account %s", address) } permN := permission.PermFlag(Uint64FromWord256(permNum)) // already shifted - if !ValidPermN(permN) { + if !permN.IsValid() { return nil, permission.ErrInvalidPermission(permN) } hasPermission := HasPermission(state, acc, permN) @@ -382,7 +382,7 @@ func setBase(stateWriter state.ReaderWriter, caller acm.Account, args []byte, ga return nil, fmt.Errorf("unknown account %s", address) } permN := permission.PermFlag(Uint64FromWord256(permNum)) - if !ValidPermN(permN) { + if !permN.IsValid() { return nil, permission.ErrInvalidPermission(permN) } permV := !permVal.IsZero() @@ -409,7 +409,7 @@ func unsetBase(stateWriter state.ReaderWriter, caller acm.Account, args []byte, return nil, fmt.Errorf("unknown account %s", address) } permN := permission.PermFlag(Uint64FromWord256(permNum)) - if !ValidPermN(permN) { + if !permN.IsValid() { return nil, permission.ErrInvalidPermission(permN) } if err = acc.MutablePermissions().Base.Unset(permN); err != nil { @@ -435,7 +435,7 @@ func setGlobal(stateWriter state.ReaderWriter, caller acm.Account, args []byte, panic("cant find the global permissions account") } permN := permission.PermFlag(Uint64FromWord256(permNum)) - if !ValidPermN(permN) { + if !permN.IsValid() { return nil, permission.ErrInvalidPermission(permN) } permV := !permVal.IsZero() @@ -517,11 +517,6 @@ func removeRole(stateWriter state.ReaderWriter, caller acm.Account, args []byte, //------------------------------------------------------------------------------------------------ // Errors and utility funcs -// Checks if a permission flag is valid (a known base chain or snative permission) -func ValidPermN(n permission.PermFlag) bool { - return n <= permission.AllPermFlags -} - // Get the global BasePermissions func globalPerms(stateWriter state.ReaderWriter) permission.BasePermissions { return state.GlobalAccountPermissions(stateWriter).Base diff --git a/execution/exec/event.go b/execution/exec/event.go index b629a265..eb320f07 100644 --- a/execution/exec/event.go +++ b/execution/exec/event.go @@ -21,6 +21,7 @@ const ( TypeAccountOutput = EventType(0x03) TypeTxExecution = EventType(0x04) TypeBlockExecution = EventType(0x05) + TypeGovernAccount = EventType(0x06) ) var nameFromType = map[EventType]string{ @@ -30,6 +31,7 @@ var nameFromType = map[EventType]string{ TypeAccountOutput: "AccountOutputEvent", TypeTxExecution: "TxExecutionEvent", TypeBlockExecution: "BlockExecutionEvent", + TypeGovernAccount: "GovernAccountEvent", } var typeFromName = make(map[string]EventType) diff --git a/execution/exec/exec.pb.go b/execution/exec/exec.pb.go index 6c7cfe26..5b4a82c9 100644 --- a/execution/exec/exec.pb.go +++ b/execution/exec/exec.pb.go @@ -16,6 +16,7 @@ Result LogEvent CallEvent + GovernAccountEvent InputEvent OutputEvent CallData @@ -32,6 +33,7 @@ import errors "github.com/hyperledger/burrow/execution/errors" import names "github.com/hyperledger/burrow/execution/names" import txs "github.com/hyperledger/burrow/txs" import permission "github.com/hyperledger/burrow/permission" +import spec "github.com/hyperledger/burrow/genesis/spec" import github_com_hyperledger_burrow_txs_payload "github.com/hyperledger/burrow/txs/payload" import github_com_hyperledger_burrow_binary "github.com/hyperledger/burrow/binary" @@ -266,11 +268,12 @@ func (*Header) XXX_MessageName() string { } type Event struct { - Header *Header `protobuf:"bytes,1,opt,name=Header" json:"Header,omitempty"` - Input *InputEvent `protobuf:"bytes,2,opt,name=Input" json:"Input,omitempty"` - Output *OutputEvent `protobuf:"bytes,3,opt,name=Output" json:"Output,omitempty"` - Call *CallEvent `protobuf:"bytes,4,opt,name=Call" json:"Call,omitempty"` - Log *LogEvent `protobuf:"bytes,5,opt,name=Log" json:"Log,omitempty"` + Header *Header `protobuf:"bytes,1,opt,name=Header" json:"Header,omitempty"` + Input *InputEvent `protobuf:"bytes,2,opt,name=Input" json:"Input,omitempty"` + Output *OutputEvent `protobuf:"bytes,3,opt,name=Output" json:"Output,omitempty"` + Call *CallEvent `protobuf:"bytes,4,opt,name=Call" json:"Call,omitempty"` + Log *LogEvent `protobuf:"bytes,5,opt,name=Log" json:"Log,omitempty"` + GovernAccount *GovernAccountEvent `protobuf:"bytes,6,opt,name=GovernAccount" json:"GovernAccount,omitempty"` } func (m *Event) Reset() { *m = Event{} } @@ -312,6 +315,13 @@ func (m *Event) GetLog() *LogEvent { return nil } +func (m *Event) GetGovernAccount() *GovernAccountEvent { + if m != nil { + return m.GovernAccount + } + return nil +} + func (*Event) XXX_MessageName() string { return "exec.Event" } @@ -410,6 +420,26 @@ func (*CallEvent) XXX_MessageName() string { return "exec.CallEvent" } +type GovernAccountEvent struct { + AccountUpdate *spec.TemplateAccount `protobuf:"bytes,1,opt,name=AccountUpdate" json:"AccountUpdate,omitempty"` +} + +func (m *GovernAccountEvent) Reset() { *m = GovernAccountEvent{} } +func (m *GovernAccountEvent) String() string { return proto.CompactTextString(m) } +func (*GovernAccountEvent) ProtoMessage() {} +func (*GovernAccountEvent) Descriptor() ([]byte, []int) { return fileDescriptorExec, []int{8} } + +func (m *GovernAccountEvent) GetAccountUpdate() *spec.TemplateAccount { + if m != nil { + return m.AccountUpdate + } + return nil +} + +func (*GovernAccountEvent) XXX_MessageName() string { + return "exec.GovernAccountEvent" +} + type InputEvent struct { Address github_com_hyperledger_burrow_crypto.Address `protobuf:"bytes,1,opt,name=Address,proto3,customtype=github.com/hyperledger/burrow/crypto.Address" json:"Address"` } @@ -417,7 +447,7 @@ type InputEvent struct { func (m *InputEvent) Reset() { *m = InputEvent{} } func (m *InputEvent) String() string { return proto.CompactTextString(m) } func (*InputEvent) ProtoMessage() {} -func (*InputEvent) Descriptor() ([]byte, []int) { return fileDescriptorExec, []int{8} } +func (*InputEvent) Descriptor() ([]byte, []int) { return fileDescriptorExec, []int{9} } func (*InputEvent) XXX_MessageName() string { return "exec.InputEvent" @@ -430,7 +460,7 @@ type OutputEvent struct { func (m *OutputEvent) Reset() { *m = OutputEvent{} } func (m *OutputEvent) String() string { return proto.CompactTextString(m) } func (*OutputEvent) ProtoMessage() {} -func (*OutputEvent) Descriptor() ([]byte, []int) { return fileDescriptorExec, []int{9} } +func (*OutputEvent) Descriptor() ([]byte, []int) { return fileDescriptorExec, []int{10} } func (*OutputEvent) XXX_MessageName() string { return "exec.OutputEvent" @@ -447,7 +477,7 @@ type CallData struct { func (m *CallData) Reset() { *m = CallData{} } func (m *CallData) String() string { return proto.CompactTextString(m) } func (*CallData) ProtoMessage() {} -func (*CallData) Descriptor() ([]byte, []int) { return fileDescriptorExec, []int{10} } +func (*CallData) Descriptor() ([]byte, []int) { return fileDescriptorExec, []int{11} } func (m *CallData) GetValue() uint64 { if m != nil { @@ -483,6 +513,8 @@ func init() { golang_proto.RegisterType((*LogEvent)(nil), "exec.LogEvent") proto.RegisterType((*CallEvent)(nil), "exec.CallEvent") golang_proto.RegisterType((*CallEvent)(nil), "exec.CallEvent") + proto.RegisterType((*GovernAccountEvent)(nil), "exec.GovernAccountEvent") + golang_proto.RegisterType((*GovernAccountEvent)(nil), "exec.GovernAccountEvent") proto.RegisterType((*InputEvent)(nil), "exec.InputEvent") golang_proto.RegisterType((*InputEvent)(nil), "exec.InputEvent") proto.RegisterType((*OutputEvent)(nil), "exec.OutputEvent") @@ -784,6 +816,16 @@ func (m *Event) MarshalTo(dAtA []byte) (int, error) { } i += n13 } + if m.GovernAccount != nil { + dAtA[i] = 0x32 + i++ + i = encodeVarintExec(dAtA, i, uint64(m.GovernAccount.Size())) + n14, err := m.GovernAccount.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n14 + } return i, nil } @@ -817,21 +859,21 @@ func (m *Result) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x1a i++ i = encodeVarintExec(dAtA, i, uint64(m.NameEntry.Size())) - n14, err := m.NameEntry.MarshalTo(dAtA[i:]) + n15, err := m.NameEntry.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n14 + i += n15 } if m.PermArgs != nil { dAtA[i] = 0x22 i++ i = encodeVarintExec(dAtA, i, uint64(m.PermArgs.Size())) - n15, err := m.PermArgs.MarshalTo(dAtA[i:]) + n16, err := m.PermArgs.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n15 + i += n16 } return i, nil } @@ -854,19 +896,19 @@ func (m *LogEvent) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintExec(dAtA, i, uint64(m.Address.Size())) - n16, err := m.Address.MarshalTo(dAtA[i:]) + n17, err := m.Address.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n16 + i += n17 dAtA[i] = 0x12 i++ i = encodeVarintExec(dAtA, i, uint64(m.Data.Size())) - n17, err := m.Data.MarshalTo(dAtA[i:]) + n18, err := m.Data.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n17 + i += n18 if len(m.Topics) > 0 { for _, msg := range m.Topics { dAtA[i] = 0x1a @@ -901,20 +943,20 @@ func (m *CallEvent) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintExec(dAtA, i, uint64(m.CallData.Size())) - n18, err := m.CallData.MarshalTo(dAtA[i:]) + n19, err := m.CallData.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n18 + i += n19 } dAtA[i] = 0x12 i++ i = encodeVarintExec(dAtA, i, uint64(m.Origin.Size())) - n19, err := m.Origin.MarshalTo(dAtA[i:]) + n20, err := m.Origin.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n19 + i += n20 if m.StackDepth != 0 { dAtA[i] = 0x18 i++ @@ -923,11 +965,39 @@ func (m *CallEvent) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x22 i++ i = encodeVarintExec(dAtA, i, uint64(m.Return.Size())) - n20, err := m.Return.MarshalTo(dAtA[i:]) + n21, err := m.Return.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n20 + i += n21 + return i, nil +} + +func (m *GovernAccountEvent) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GovernAccountEvent) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.AccountUpdate != nil { + dAtA[i] = 0xa + i++ + i = encodeVarintExec(dAtA, i, uint64(m.AccountUpdate.Size())) + n22, err := m.AccountUpdate.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n22 + } return i, nil } @@ -949,11 +1019,11 @@ func (m *InputEvent) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintExec(dAtA, i, uint64(m.Address.Size())) - n21, err := m.Address.MarshalTo(dAtA[i:]) + n23, err := m.Address.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n21 + i += n23 return i, nil } @@ -975,11 +1045,11 @@ func (m *OutputEvent) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintExec(dAtA, i, uint64(m.Address.Size())) - n22, err := m.Address.MarshalTo(dAtA[i:]) + n24, err := m.Address.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n22 + i += n24 return i, nil } @@ -1001,27 +1071,27 @@ func (m *CallData) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintExec(dAtA, i, uint64(m.Caller.Size())) - n23, err := m.Caller.MarshalTo(dAtA[i:]) + n25, err := m.Caller.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n23 + i += n25 dAtA[i] = 0x12 i++ i = encodeVarintExec(dAtA, i, uint64(m.Callee.Size())) - n24, err := m.Callee.MarshalTo(dAtA[i:]) + n26, err := m.Callee.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n24 + i += n26 dAtA[i] = 0x1a i++ i = encodeVarintExec(dAtA, i, uint64(m.Data.Size())) - n25, err := m.Data.MarshalTo(dAtA[i:]) + n27, err := m.Data.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n25 + i += n27 if m.Value != 0 { dAtA[i] = 0x20 i++ @@ -1166,6 +1236,10 @@ func (m *Event) Size() (n int) { l = m.Log.Size() n += 1 + l + sovExec(uint64(l)) } + if m.GovernAccount != nil { + l = m.GovernAccount.Size() + n += 1 + l + sovExec(uint64(l)) + } return n } @@ -1223,6 +1297,16 @@ func (m *CallEvent) Size() (n int) { return n } +func (m *GovernAccountEvent) Size() (n int) { + var l int + _ = l + if m.AccountUpdate != nil { + l = m.AccountUpdate.Size() + n += 1 + l + sovExec(uint64(l)) + } + return n +} + func (m *InputEvent) Size() (n int) { var l int _ = l @@ -2213,6 +2297,39 @@ func (m *Event) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GovernAccount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowExec + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthExec + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.GovernAccount == nil { + m.GovernAccount = &GovernAccountEvent{} + } + if err := m.GovernAccount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipExec(dAtA[iNdEx:]) @@ -2704,6 +2821,89 @@ func (m *CallEvent) Unmarshal(dAtA []byte) error { } return nil } +func (m *GovernAccountEvent) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowExec + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GovernAccountEvent: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GovernAccountEvent: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccountUpdate", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowExec + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthExec + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AccountUpdate == nil { + m.AccountUpdate = &spec.TemplateAccount{} + } + if err := m.AccountUpdate.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipExec(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthExec + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *InputEvent) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -3151,66 +3351,68 @@ func init() { proto.RegisterFile("exec.proto", fileDescriptorExec) } func init() { golang_proto.RegisterFile("exec.proto", fileDescriptorExec) } var fileDescriptorExec = []byte{ - // 963 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcd, 0x6f, 0x1b, 0x45, - 0x14, 0xef, 0xf8, 0x63, 0x6d, 0x3f, 0x3b, 0x25, 0x8c, 0x22, 0xb4, 0xea, 0xc1, 0x6b, 0xa5, 0xa8, - 0x0a, 0xa1, 0x5d, 0x23, 0x97, 0x20, 0xc1, 0xad, 0x26, 0x56, 0x93, 0x2a, 0x24, 0x68, 0x6a, 0x40, - 0x70, 0x5b, 0xef, 0x0e, 0x9b, 0x25, 0xf6, 0xee, 0x6a, 0x76, 0xb6, 0xac, 0x8f, 0x70, 0xe2, 0xc8, - 0x81, 0x03, 0xc7, 0xe6, 0x02, 0xff, 0x06, 0xc7, 0x1c, 0xb9, 0x52, 0x24, 0x0b, 0x25, 0xff, 0x45, - 0x4f, 0x68, 0x3e, 0x76, 0xbd, 0x8e, 0xaa, 0xb6, 0x6a, 0xd2, 0x4b, 0x34, 0xef, 0xbd, 0xdf, 0xfe, - 0xe6, 0x7d, 0xfc, 0xe6, 0xc5, 0x00, 0x34, 0xa3, 0xae, 0x1d, 0xb3, 0x88, 0x47, 0xb8, 0x26, 0xce, - 0xb7, 0xee, 0xf9, 0x01, 0x3f, 0x4e, 0x27, 0xb6, 0x1b, 0xcd, 0xfa, 0x7e, 0xe4, 0x47, 0x7d, 0x19, - 0x9c, 0xa4, 0xdf, 0x4b, 0x4b, 0x1a, 0xf2, 0xa4, 0x3e, 0xba, 0x75, 0xb7, 0x04, 0xe7, 0x34, 0xf4, - 0x28, 0x9b, 0x05, 0x21, 0xef, 0x3b, 0x13, 0x37, 0xe8, 0xf3, 0x79, 0x4c, 0x13, 0xf5, 0x57, 0xa3, - 0x3b, 0x94, 0xb1, 0x88, 0xe5, 0x56, 0x3b, 0x74, 0x66, 0x45, 0xa8, 0xc5, 0xb3, 0xfc, 0xb8, 0x1e, - 0x0b, 0x96, 0x24, 0x09, 0xa2, 0x50, 0x79, 0x36, 0x7f, 0x43, 0x70, 0x73, 0x38, 0x8d, 0xdc, 0x93, - 0x51, 0x46, 0xdd, 0x94, 0x07, 0x51, 0x88, 0xdf, 0x03, 0x63, 0x8f, 0x06, 0xfe, 0x31, 0x37, 0x51, - 0x0f, 0x6d, 0xd5, 0x88, 0xb6, 0xf0, 0x7d, 0x68, 0x4b, 0xe4, 0x1e, 0x75, 0x3c, 0xca, 0xcc, 0x4a, - 0x0f, 0x6d, 0xb5, 0x07, 0xef, 0xda, 0xb2, 0xce, 0x52, 0x80, 0x94, 0x51, 0x78, 0x07, 0x3a, 0xe3, - 0xac, 0xe0, 0x4e, 0xcc, 0x6a, 0xaf, 0xba, 0xfc, 0xaa, 0x14, 0x21, 0x2b, 0xb0, 0xcd, 0x4f, 0x57, - 0xee, 0xc2, 0x18, 0x6a, 0x8f, 0x1e, 0x1f, 0x1d, 0xca, 0x84, 0x5a, 0x44, 0x9e, 0x45, 0x9a, 0x87, - 0xe9, 0x6c, 0x9c, 0x25, 0x32, 0x93, 0x3a, 0xd1, 0xd6, 0xe6, 0x45, 0x15, 0xda, 0x25, 0x2e, 0xfc, - 0x08, 0x8c, 0x71, 0x36, 0x9e, 0xc7, 0x54, 0xe2, 0xd6, 0x86, 0x83, 0xe7, 0x0b, 0xcb, 0x2e, 0xf5, - 0xf6, 0x78, 0x1e, 0x53, 0x36, 0xa5, 0x9e, 0x4f, 0x59, 0x7f, 0x92, 0x32, 0x16, 0xfd, 0xd8, 0xe7, - 0x59, 0xd2, 0x8f, 0x9d, 0xf9, 0x34, 0x72, 0x3c, 0x5b, 0x7c, 0x49, 0x34, 0x03, 0xfe, 0x42, 0x70, - 0xed, 0x39, 0xc9, 0xb1, 0x59, 0xed, 0xa1, 0xad, 0xce, 0x70, 0xe7, 0x6c, 0x61, 0xdd, 0x78, 0xb6, - 0xb0, 0xee, 0xbd, 0x9c, 0x6f, 0x12, 0x84, 0x0e, 0x9b, 0xdb, 0x7b, 0x34, 0x1b, 0xce, 0x39, 0x4d, - 0x88, 0x26, 0x29, 0x75, 0xba, 0xb6, 0xd2, 0xe9, 0x0d, 0xa8, 0xef, 0x87, 0x1e, 0xcd, 0xcc, 0xba, - 0x74, 0x2b, 0x03, 0x7f, 0x0b, 0xcd, 0x51, 0xf8, 0x84, 0x4e, 0xa3, 0x98, 0x9a, 0x86, 0x6c, 0xfe, - 0x9a, 0x2d, 0x46, 0x9b, 0x3b, 0x87, 0xf6, 0xb3, 0x85, 0xb5, 0xfd, 0xca, 0xca, 0x0a, 0x3c, 0x29, - 0xe8, 0xf0, 0x6d, 0x30, 0x46, 0x4f, 0x68, 0xc8, 0x13, 0xb3, 0x21, 0xe7, 0xd3, 0x56, 0xf3, 0x91, - 0x3e, 0xa2, 0x43, 0xf8, 0x7d, 0x30, 0x08, 0x4d, 0xd2, 0x29, 0x37, 0x9b, 0xf2, 0xf6, 0x8e, 0x02, - 0x29, 0x1f, 0xd1, 0x31, 0x7c, 0x07, 0x1a, 0x84, 0xba, 0x34, 0x88, 0xb9, 0xd9, 0xd2, 0x30, 0x71, - 0xa9, 0xf6, 0x91, 0x3c, 0x88, 0xfb, 0xd0, 0x1a, 0x65, 0x2e, 0x8d, 0xc5, 0x8c, 0x4c, 0xc8, 0xb5, - 0xa4, 0x44, 0x5c, 0x04, 0xc8, 0x12, 0xf3, 0x59, 0xe7, 0x97, 0x53, 0x0b, 0xfd, 0x7a, 0x6a, 0xa1, - 0xa7, 0xa7, 0x16, 0xda, 0xfc, 0xb7, 0x22, 0x7a, 0x27, 0xc5, 0xb1, 0x1c, 0x30, 0xba, 0xc6, 0x01, - 0x57, 0xae, 0x63, 0xc0, 0x1f, 0x42, 0x4b, 0x36, 0x4f, 0x66, 0x57, 0x95, 0xd9, 0xad, 0x3d, 0x5f, - 0x58, 0x4b, 0x27, 0x59, 0x1e, 0xb1, 0x09, 0x0d, 0x69, 0xec, 0xef, 0x4a, 0x39, 0xb4, 0x48, 0x6e, - 0x96, 0x74, 0x52, 0x7f, 0xb1, 0x4e, 0x8c, 0xb2, 0x4e, 0x56, 0x3a, 0xdb, 0x78, 0x8d, 0xce, 0xae, - 0xff, 0xfe, 0xd4, 0xba, 0xb1, 0xd2, 0xdd, 0x7f, 0x10, 0xd4, 0xe5, 0xe5, 0x62, 0xe8, 0xfa, 0xbd, - 0xa3, 0xf2, 0xd0, 0xf5, 0x53, 0xcf, 0x47, 0x70, 0x47, 0x24, 0x12, 0xa7, 0x5c, 0x2f, 0x85, 0x75, - 0x05, 0x92, 0x2e, 0xa5, 0x21, 0x15, 0xc6, 0x1f, 0x80, 0x71, 0x94, 0x72, 0x01, 0xac, 0x96, 0xb7, - 0x87, 0xf2, 0x69, 0xb5, 0x29, 0x03, 0xdf, 0x86, 0xda, 0xe7, 0xce, 0x74, 0x2a, 0x5b, 0xd1, 0x1e, - 0xbc, 0xa3, 0x80, 0xc2, 0xa3, 0x60, 0x32, 0x88, 0x7b, 0x50, 0x3d, 0x88, 0x7c, 0xd9, 0x95, 0xf6, - 0xe0, 0xa6, 0xc2, 0x1c, 0x44, 0xbe, 0x82, 0x88, 0xd0, 0x0b, 0x6a, 0xfb, 0x03, 0xe5, 0x3a, 0x16, - 0x7d, 0x25, 0x94, 0xa7, 0x2c, 0x94, 0xc5, 0x75, 0x88, 0xb6, 0xc4, 0x24, 0x1e, 0x3a, 0xc9, 0x57, - 0x09, 0xf5, 0x64, 0x41, 0x35, 0x92, 0x9b, 0x78, 0x1b, 0x5a, 0x87, 0xce, 0x8c, 0x8e, 0x42, 0xce, - 0xe6, 0xba, 0x86, 0x8e, 0xad, 0x96, 0xad, 0xf4, 0x91, 0x65, 0x18, 0x7f, 0x04, 0xcd, 0x2f, 0x29, - 0x9b, 0x3d, 0x60, 0x7e, 0xa2, 0xab, 0xd8, 0xb0, 0x4b, 0xfb, 0x37, 0x8f, 0x91, 0x02, 0x75, 0x49, - 0xe2, 0x3f, 0x55, 0xa0, 0x99, 0x17, 0x83, 0x0f, 0xa1, 0xf1, 0xc0, 0xf3, 0x18, 0x4d, 0x12, 0x95, - 0xeb, 0xf0, 0x63, 0xad, 0xcc, 0xbb, 0x2f, 0x57, 0xa6, 0xcb, 0xe6, 0x31, 0x8f, 0x6c, 0xfd, 0x2d, - 0xc9, 0x49, 0xf0, 0x3e, 0xd4, 0x76, 0x1d, 0xee, 0x5c, 0x4d, 0xe6, 0x92, 0x02, 0x1f, 0x80, 0x31, - 0x8e, 0xe2, 0xc0, 0x55, 0xcb, 0xfd, 0xb5, 0x33, 0xd3, 0x64, 0xdf, 0x44, 0xcc, 0x1b, 0xec, 0x7c, - 0x42, 0x34, 0xc7, 0xa5, 0x1e, 0xfc, 0x5c, 0x81, 0x56, 0x31, 0x74, 0xbc, 0x0d, 0x4d, 0x61, 0xc8, - 0xc4, 0x51, 0x79, 0xe6, 0xb9, 0x97, 0x14, 0x71, 0x91, 0xd5, 0x11, 0x0b, 0xfc, 0x20, 0xd4, 0x25, - 0xbe, 0x59, 0xbf, 0x34, 0x07, 0xee, 0x02, 0x3c, 0xe6, 0x8e, 0x7b, 0xb2, 0x4b, 0x63, 0xae, 0x96, - 0x7f, 0x8d, 0x94, 0x3c, 0x62, 0x6f, 0x68, 0x25, 0xd5, 0xae, 0xb4, 0x37, 0x14, 0xc9, 0xa5, 0x26, - 0xfc, 0x00, 0xb0, 0x7c, 0x4a, 0xd7, 0xad, 0x84, 0x4b, 0x77, 0x9d, 0x40, 0xbb, 0xf4, 0x1a, 0xdf, - 0xf2, 0x65, 0x7f, 0x56, 0x60, 0x65, 0x60, 0xe2, 0xac, 0x37, 0xcd, 0x1b, 0x0f, 0x4c, 0x71, 0x14, - 0x6c, 0xf4, 0x6a, 0xe3, 0x57, 0x1c, 0xc5, 0x6b, 0xa9, 0x5e, 0xfd, 0xb5, 0x6c, 0x40, 0xfd, 0x6b, - 0x67, 0x9a, 0x52, 0xfd, 0x2f, 0x5f, 0x19, 0x78, 0x1d, 0xaa, 0x0f, 0x9d, 0x44, 0xaf, 0x77, 0x71, - 0x5c, 0xed, 0xd4, 0x70, 0x78, 0x76, 0xde, 0x45, 0x7f, 0x9f, 0x77, 0xd1, 0x7f, 0xe7, 0x5d, 0xf4, - 0xd7, 0x45, 0x17, 0x9d, 0x5d, 0x74, 0xd1, 0x77, 0xaf, 0x28, 0x86, 0xe6, 0xbf, 0x81, 0xe4, 0x69, - 0x62, 0xc8, 0x5f, 0x7c, 0xf7, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x4c, 0xf0, 0xd7, 0x87, 0x9a, - 0x0a, 0x00, 0x00, + // 999 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x6f, 0x1b, 0x45, + 0x14, 0xef, 0xda, 0xeb, 0x7f, 0xcf, 0x76, 0x09, 0xa3, 0x80, 0x56, 0x3d, 0xd8, 0xd6, 0x16, 0x55, + 0x21, 0xb4, 0x6b, 0xe4, 0x12, 0x24, 0x40, 0x42, 0x8a, 0x89, 0x95, 0xa4, 0x0a, 0x09, 0x4c, 0x5d, + 0x10, 0x08, 0x0e, 0xeb, 0xdd, 0xc1, 0x59, 0xd5, 0xde, 0x59, 0xcd, 0xce, 0x86, 0xf5, 0x87, 0xe0, + 0xc6, 0xa1, 0xdc, 0xf8, 0x28, 0x1c, 0x73, 0x83, 0x0b, 0x97, 0x1e, 0x2c, 0x94, 0x7e, 0x04, 0x38, + 0xf5, 0x84, 0xe6, 0xcf, 0xae, 0xd7, 0x02, 0x35, 0x55, 0x92, 0x8b, 0x35, 0xef, 0xbd, 0xdf, 0xfc, + 0xe6, 0xcd, 0x7b, 0xbf, 0x37, 0x5e, 0x00, 0x92, 0x12, 0xcf, 0x89, 0x18, 0xe5, 0x14, 0x99, 0x62, + 0x7d, 0xe7, 0xc1, 0x34, 0xe0, 0xa7, 0xc9, 0xc4, 0xf1, 0xe8, 0xbc, 0x3f, 0xa5, 0x53, 0xda, 0x97, + 0xc1, 0x49, 0xf2, 0x83, 0xb4, 0xa4, 0x21, 0x57, 0x6a, 0xd3, 0x9d, 0xfb, 0x05, 0x38, 0x27, 0xa1, + 0x4f, 0xd8, 0x3c, 0x08, 0x79, 0xdf, 0x9d, 0x78, 0x41, 0x9f, 0x2f, 0x22, 0x12, 0xab, 0x5f, 0x8d, + 0x6e, 0x11, 0xc6, 0x28, 0xcb, 0xac, 0x66, 0xe8, 0xce, 0xf3, 0x50, 0x83, 0xa7, 0xd9, 0x72, 0x23, + 0x12, 0x2c, 0x71, 0x1c, 0xd0, 0x50, 0x7b, 0x20, 0x8e, 0xb2, 0x34, 0xed, 0x9f, 0x0d, 0xb8, 0x3d, + 0x9c, 0x51, 0xef, 0xe9, 0x28, 0x25, 0x5e, 0xc2, 0x03, 0x1a, 0xa2, 0xb7, 0xa1, 0x7a, 0x40, 0x82, + 0xe9, 0x29, 0xb7, 0x8c, 0x9e, 0xb1, 0x65, 0x62, 0x6d, 0xa1, 0x87, 0xd0, 0x94, 0xc8, 0x03, 0xe2, + 0xfa, 0x84, 0x59, 0xa5, 0x9e, 0xb1, 0xd5, 0x1c, 0xbc, 0xe9, 0xc8, 0x3b, 0x17, 0x02, 0xb8, 0x88, + 0x42, 0x3b, 0xd0, 0x1a, 0xa7, 0x39, 0x77, 0x6c, 0x95, 0x7b, 0xe5, 0xd5, 0xae, 0x42, 0x04, 0xaf, + 0xc1, 0xec, 0x8f, 0xd6, 0xce, 0x42, 0x08, 0xcc, 0x47, 0x8f, 0x4f, 0x8e, 0x65, 0x42, 0x0d, 0x2c, + 0xd7, 0x22, 0xcd, 0xe3, 0x64, 0x3e, 0x4e, 0x63, 0x99, 0x49, 0x05, 0x6b, 0xcb, 0xfe, 0xb3, 0x0c, + 0xcd, 0x02, 0x17, 0x7a, 0x04, 0xd5, 0x71, 0x3a, 0x5e, 0x44, 0x44, 0xe2, 0xda, 0xc3, 0xc1, 0xcb, + 0x65, 0xd7, 0x29, 0xd4, 0xf9, 0x74, 0x11, 0x11, 0x36, 0x23, 0xfe, 0x94, 0xb0, 0xfe, 0x24, 0x61, + 0x8c, 0xfe, 0xd8, 0xe7, 0x69, 0xdc, 0x8f, 0xdc, 0xc5, 0x8c, 0xba, 0xbe, 0x23, 0x76, 0x62, 0xcd, + 0x80, 0x3e, 0x17, 0x5c, 0x07, 0x6e, 0x7c, 0x6a, 0x95, 0x7b, 0xc6, 0x56, 0x6b, 0xb8, 0x73, 0xbe, + 0xec, 0xde, 0x7a, 0xbe, 0xec, 0x3e, 0x78, 0x35, 0xdf, 0x24, 0x08, 0x5d, 0xb6, 0x70, 0x0e, 0x48, + 0x3a, 0x5c, 0x70, 0x12, 0x63, 0x4d, 0x52, 0xa8, 0xb4, 0xb9, 0x56, 0xe9, 0x4d, 0xa8, 0x1c, 0x86, + 0x3e, 0x49, 0xad, 0x8a, 0x74, 0x2b, 0x03, 0x7d, 0x03, 0xf5, 0x51, 0x78, 0x46, 0x66, 0x34, 0x22, + 0x56, 0x55, 0x16, 0xbf, 0xed, 0x88, 0x36, 0x67, 0xce, 0xa1, 0xf3, 0x7c, 0xd9, 0xdd, 0xbe, 0xf4, + 0x66, 0x39, 0x1e, 0xe7, 0x74, 0xe8, 0x2e, 0x54, 0x47, 0x67, 0x24, 0xe4, 0xb1, 0x55, 0x93, 0xfd, + 0x69, 0xaa, 0xfe, 0x48, 0x1f, 0xd6, 0x21, 0xf4, 0x0e, 0x54, 0x31, 0x89, 0x93, 0x19, 0xb7, 0xea, + 0xf2, 0xf4, 0x96, 0x02, 0x29, 0x1f, 0xd6, 0x31, 0x74, 0x0f, 0x6a, 0x98, 0x78, 0x24, 0x88, 0xb8, + 0xd5, 0xd0, 0x30, 0x71, 0xa8, 0xf6, 0xe1, 0x2c, 0x88, 0xfa, 0xd0, 0x18, 0xa5, 0x1e, 0x89, 0x44, + 0x8f, 0x2c, 0xc8, 0xb4, 0xa4, 0x04, 0x9d, 0x07, 0xf0, 0x0a, 0x63, 0xff, 0x5e, 0x12, 0xd5, 0x92, + 0x72, 0x58, 0xb5, 0xd4, 0xb8, 0xc1, 0x96, 0x96, 0x6e, 0xa2, 0xa5, 0xef, 0x41, 0x43, 0x96, 0x4b, + 0x66, 0x57, 0x96, 0xd9, 0xb5, 0x5f, 0x2e, 0xbb, 0x2b, 0x27, 0x5e, 0x2d, 0x91, 0x05, 0x35, 0x69, + 0x1c, 0xee, 0x49, 0x01, 0x34, 0x70, 0x66, 0x16, 0x94, 0x51, 0xf9, 0x7f, 0x65, 0x54, 0x8b, 0xca, + 0x58, 0xab, 0x65, 0xed, 0xf2, 0x5a, 0x7e, 0x6c, 0x3e, 0xfb, 0xb5, 0x7b, 0xcb, 0xfe, 0xa9, 0x04, + 0x15, 0x79, 0xa0, 0x68, 0xad, 0x9e, 0x6a, 0xa3, 0xd8, 0x5a, 0x3d, 0xd0, 0x59, 0xd9, 0xef, 0x89, + 0xc3, 0xa3, 0x84, 0xeb, 0xd1, 0xdf, 0x50, 0x20, 0xe9, 0x52, 0x4a, 0x51, 0x61, 0xf4, 0x2e, 0x54, + 0x4f, 0x12, 0x2e, 0x80, 0xe5, 0xe2, 0x1b, 0xa1, 0x7c, 0x5a, 0x53, 0xca, 0x40, 0x77, 0xc1, 0xfc, + 0xcc, 0x9d, 0xcd, 0xe4, 0xf5, 0x9b, 0x83, 0x37, 0x14, 0x50, 0x78, 0x14, 0x4c, 0x06, 0x51, 0x0f, + 0xca, 0x47, 0x74, 0x2a, 0x2b, 0xd1, 0x1c, 0xdc, 0x56, 0x98, 0x23, 0x3a, 0x55, 0x10, 0x11, 0x42, + 0x9f, 0x42, 0x7b, 0x9f, 0x9e, 0x11, 0x16, 0xee, 0x7a, 0x1e, 0x4d, 0x42, 0xae, 0xe7, 0xc3, 0x52, + 0xd8, 0xb5, 0x90, 0xda, 0xb5, 0x0e, 0xd7, 0xf5, 0x78, 0x66, 0x64, 0x0a, 0x17, 0xf5, 0xc7, 0x84, + 0x27, 0x2c, 0x94, 0x05, 0x69, 0x61, 0x6d, 0x89, 0x8e, 0xed, 0xbb, 0xf1, 0x93, 0x98, 0xf8, 0xb2, + 0x08, 0x26, 0xce, 0x4c, 0xb4, 0x0d, 0x8d, 0x63, 0x77, 0x4e, 0x46, 0x21, 0x67, 0x0b, 0x7d, 0xef, + 0x96, 0xa3, 0x9e, 0x64, 0xe9, 0xc3, 0xab, 0x30, 0x7a, 0x1f, 0xea, 0x5f, 0x10, 0x36, 0xdf, 0x65, + 0xd3, 0x58, 0xdf, 0x7c, 0xd3, 0x29, 0xbc, 0xd2, 0x59, 0x0c, 0xe7, 0x28, 0xfb, 0x6f, 0x03, 0xea, + 0xd9, 0x95, 0xd1, 0x31, 0xd4, 0x76, 0x7d, 0x9f, 0x91, 0x38, 0x56, 0xd9, 0x0d, 0x3f, 0xd0, 0x9a, + 0xbd, 0xff, 0x6a, 0xcd, 0x7a, 0x6c, 0x11, 0x71, 0xea, 0xe8, 0xbd, 0x38, 0x23, 0x41, 0x87, 0x60, + 0xee, 0xb9, 0xdc, 0xbd, 0xde, 0x00, 0x48, 0x0a, 0x74, 0x04, 0xd5, 0x31, 0x8d, 0x02, 0x4f, 0x3d, + 0xf4, 0xaf, 0x9d, 0x99, 0x26, 0xfb, 0x9a, 0x32, 0x7f, 0xb0, 0xf3, 0x21, 0xd6, 0x1c, 0xf6, 0x3f, + 0x06, 0x34, 0x72, 0x31, 0xa0, 0x6d, 0xa8, 0x0b, 0x43, 0xa6, 0x6a, 0x14, 0xb5, 0x90, 0x79, 0x71, + 0x1e, 0x17, 0x79, 0x9c, 0xb0, 0x60, 0x1a, 0x84, 0xfa, 0x52, 0x57, 0xab, 0x90, 0xe6, 0x40, 0x1d, + 0x80, 0xc7, 0xdc, 0xf5, 0x9e, 0xee, 0x91, 0x88, 0xab, 0xa7, 0xdf, 0xc4, 0x05, 0x8f, 0x78, 0x43, + 0xb4, 0x5a, 0xcc, 0x6b, 0xbd, 0x21, 0x8a, 0xc4, 0xfe, 0x12, 0xd0, 0x7f, 0x25, 0x8b, 0x3e, 0x81, + 0xb6, 0xb6, 0x9f, 0x44, 0xbe, 0xcb, 0x89, 0xae, 0xc1, 0x5b, 0x8e, 0xfc, 0x37, 0x1f, 0x93, 0x79, + 0x34, 0x73, 0x39, 0xd1, 0x10, 0xbc, 0x8e, 0xb5, 0xbf, 0x03, 0x58, 0xcd, 0xe9, 0x4d, 0x0b, 0xc8, + 0xfe, 0x1e, 0x9a, 0x85, 0xe1, 0xbe, 0x71, 0xfa, 0x5f, 0x4a, 0xb0, 0xd6, 0x59, 0xb1, 0xd6, 0x4f, + 0xd5, 0x95, 0x3b, 0xab, 0x38, 0x72, 0x36, 0x72, 0x3d, 0x9d, 0x28, 0x8e, 0x7c, 0x90, 0xca, 0xd7, + 0x1f, 0xa4, 0x4d, 0xa8, 0x7c, 0xe5, 0xce, 0x12, 0xa2, 0xbf, 0x0c, 0x94, 0x81, 0x36, 0xa0, 0xbc, + 0xef, 0xc6, 0xfa, 0x3f, 0x41, 0x2c, 0x87, 0xc3, 0xf3, 0x8b, 0x8e, 0xf1, 0xc7, 0x45, 0xc7, 0xf8, + 0xeb, 0xa2, 0x63, 0xfc, 0xf6, 0xa2, 0x63, 0x9c, 0xbf, 0xe8, 0x18, 0xdf, 0x5e, 0x92, 0x3e, 0xc9, + 0x3e, 0x8e, 0xe4, 0x6a, 0x52, 0x95, 0x9f, 0x82, 0x0f, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x47, + 0x90, 0xbb, 0xef, 0xbf, 0x0a, 0x00, 0x00, } diff --git a/execution/exec/govern_account_event.go b/execution/exec/govern_account_event.go new file mode 100644 index 00000000..81c33c44 --- /dev/null +++ b/execution/exec/govern_account_event.go @@ -0,0 +1 @@ +package exec diff --git a/execution/exec/tx_execution.go b/execution/exec/tx_execution.go index 493af292..9de377ff 100644 --- a/execution/exec/tx_execution.go +++ b/execution/exec/tx_execution.go @@ -15,9 +15,10 @@ import ( func EventStringAccountInput(addr crypto.Address) string { return fmt.Sprintf("Acc/%s/Input", addr) } func EventStringAccountOutput(addr crypto.Address) string { return fmt.Sprintf("Acc/%s/Output", addr) } -func EventStringAccountCall(addr crypto.Address) string { return fmt.Sprintf("Acc/%s/Call", addr) } -func EventStringLogEvent(addr crypto.Address) string { return fmt.Sprintf("Log/%s", addr) } -func EventStringTxExecution(txHash []byte) string { return fmt.Sprintf("Execution/Tx/%X", txHash) } +func EventStringAccountCall(addr crypto.Address) string { return fmt.Sprintf("Acc/%s/Call", addr) } +func EventStringLogEvent(addr crypto.Address) string { return fmt.Sprintf("Log/%s", addr) } +func EventStringTxExecution(txHash []byte) string { return fmt.Sprintf("Execution/Tx/%X", txHash) } +func EventStringGovernAccount(addr *crypto.Address) string { return fmt.Sprintf("Govern/Acc/%v", addr) } func NewTxExecution(txEnv *txs.Envelope) *TxExecution { return &TxExecution{ @@ -89,6 +90,13 @@ func (txe *TxExecution) Call(call *CallEvent, exception *errors.Exception) { }) } +func (txe *TxExecution) GovernAccount(governAccount *GovernAccountEvent, exception *errors.Exception) { + txe.Append(&Event{ + Header: txe.Header(TypeCall, EventStringGovernAccount(governAccount.AccountUpdate.Address), exception), + GovernAccount: governAccount, + }) +} + // Set result func (txe *TxExecution) Return(returnValue []byte, gasUsed uint64) { if txe.Result == nil { diff --git a/execution/execution.go b/execution/execution.go index 23412014..d88d45bc 100644 --- a/execution/execution.go +++ b/execution/execution.go @@ -18,6 +18,7 @@ import ( "fmt" "runtime/debug" "sync" + "time" "context" @@ -27,14 +28,15 @@ import ( bcm "github.com/hyperledger/burrow/blockchain" "github.com/hyperledger/burrow/crypto" "github.com/hyperledger/burrow/event" + "github.com/hyperledger/burrow/execution/contexts" "github.com/hyperledger/burrow/execution/evm" "github.com/hyperledger/burrow/execution/exec" - "github.com/hyperledger/burrow/execution/executors" "github.com/hyperledger/burrow/execution/names" "github.com/hyperledger/burrow/logging" "github.com/hyperledger/burrow/logging/structure" "github.com/hyperledger/burrow/txs" "github.com/hyperledger/burrow/txs/payload" + "github.com/pkg/errors" abciTypes "github.com/tendermint/abci/types" ) @@ -67,13 +69,13 @@ type BatchCommitter interface { BatchExecutor // Commit execution results to underlying State and provide opportunity // to mutate state before it is saved - Commit(*abciTypes.Header) (stateHash []byte, err error) + Commit(blockHash []byte, blockTime time.Time, header *abciTypes.Header) (stateHash []byte, err error) } type executor struct { sync.RWMutex runCall bool - tip bcm.TipInfo + blockchain *bcm.Blockchain state ExecutorState stateCache *state.Cache nameRegCache *names.Cache @@ -87,32 +89,32 @@ type executor struct { var _ BatchExecutor = (*executor)(nil) // Wraps a cache of what is variously known as the 'check cache' and 'mempool' -func NewBatchChecker(backend ExecutorState, tip bcm.TipInfo, logger *logging.Logger, +func NewBatchChecker(backend ExecutorState, blockchain *bcm.Blockchain, logger *logging.Logger, options ...ExecutionOption) BatchExecutor { - return newExecutor("CheckCache", false, backend, tip, event.NewNoOpPublisher(), + return newExecutor("CheckCache", false, backend, blockchain, event.NewNoOpPublisher(), logger.WithScope("NewBatchExecutor"), options...) } -func NewBatchCommitter(backend ExecutorState, tip bcm.TipInfo, emitter event.Publisher, logger *logging.Logger, +func NewBatchCommitter(backend ExecutorState, blockchain *bcm.Blockchain, emitter event.Publisher, logger *logging.Logger, options ...ExecutionOption) BatchCommitter { - return newExecutor("CommitCache", true, backend, tip, emitter, + return newExecutor("CommitCache", true, backend, blockchain, emitter, logger.WithScope("NewBatchCommitter"), options...) } -func newExecutor(name string, runCall bool, backend ExecutorState, tip bcm.TipInfo, publisher event.Publisher, +func newExecutor(name string, runCall bool, backend ExecutorState, blockchain *bcm.Blockchain, publisher event.Publisher, logger *logging.Logger, options ...ExecutionOption) *executor { exe := &executor{ runCall: runCall, state: backend, - tip: tip, + blockchain: blockchain, stateCache: state.NewCache(backend, state.Name(name)), nameRegCache: names.NewCache(backend), publisher: publisher, blockExecution: &exec.BlockExecution{ - Height: tip.LastBlockHeight() + 1, + Height: blockchain.LastBlockHeight() + 1, }, logger: logger.With(structure.ComponentKey, "Executor"), } @@ -120,29 +122,34 @@ func newExecutor(name string, runCall bool, backend ExecutorState, tip bcm.TipIn option(exe) } exe.txExecutors = map[payload.Type]Context{ - payload.TypeSend: &executors.SendContext{ - Tip: tip, + payload.TypeSend: &contexts.SendContext{ + Tip: blockchain, StateWriter: exe.stateCache, Logger: exe.logger, }, - payload.TypeCall: &executors.CallContext{ - Tip: tip, + payload.TypeCall: &contexts.CallContext{ + Tip: blockchain, StateWriter: exe.stateCache, RunCall: runCall, VMOptions: exe.vmOptions, Logger: exe.logger, }, - payload.TypeName: &executors.NameContext{ - Tip: tip, + payload.TypeName: &contexts.NameContext{ + Tip: blockchain, StateWriter: exe.stateCache, NameReg: exe.nameRegCache, Logger: exe.logger, }, - payload.TypePermissions: &executors.PermissionsContext{ - Tip: tip, + payload.TypePermissions: &contexts.PermissionsContext{ + Tip: blockchain, StateWriter: exe.stateCache, Logger: exe.logger, }, + payload.TypeGovernance: &contexts.GovernanceContext{ + ValidatorSet: blockchain, + StateWriter: exe.stateCache, + Logger: exe.logger, + }, } return exe } @@ -198,7 +205,8 @@ func (exe *executor) finaliseBlockExecution(header *abciTypes.Header) (*exec.Blo return be, nil } -func (exe *executor) Commit(header *abciTypes.Header) (_ []byte, err error) { +func (exe *executor) Commit(blockHash []byte, blockTime time.Time, header *abciTypes.Header) (_ []byte, err error) { + // The write lock to the executor is controlled by the caller (e.g. abci.App) so we do not acquire it here to avoid // deadlock defer func() { @@ -212,6 +220,9 @@ func (exe *executor) Commit(header *abciTypes.Header) (_ []byte, err error) { if err != nil { return nil, err } + + // First commit the app state, this app hash will not get checkpointed until the next block when we are sure + // that nothing in the downstream commit process could have failed. At worst we go back one block. hash, err := exe.state.Update(func(ws Updatable) error { // flush the caches err := exe.stateCache.Flush(ws, exe.state) @@ -242,6 +253,14 @@ func (exe *executor) Commit(header *abciTypes.Header) (_ []byte, err error) { exe.logger.InfoMsg("Error publishing TxExecution", "height", blockExecution.Height, structure.ErrorKey, publishErr) + + // Commit to our blockchain state which will checkpoint the previous app hash by saving it to the database + // (we know the previous app hash is safely committed because we are about to commit the next) + err = exe.blockchain.CommitBlock(blockTime, blockHash, hash) + if err != nil { + panic(errors.Wrap(err, "could not commit block to blockchain state")) + } + return hash, nil } diff --git a/execution/execution_test.go b/execution/execution_test.go index 608da343..033c5541 100644 --- a/execution/execution_test.go +++ b/execution/execution_test.go @@ -123,15 +123,13 @@ var testChainID = testGenesisDoc.ChainID() type testExecutor struct { *executor - blockchain *bcm.Blockchain } func makeExecutor(state *State) *testExecutor { blockchain := newBlockchain(testGenesisDoc) return &testExecutor{ - executor: newExecutor("makeExecutorCache", true, state, blockchain.Tip, event.NewNoOpPublisher(), + executor: newExecutor("makeExecutorCache", true, state, blockchain, event.NewNoOpPublisher(), logger), - blockchain: blockchain, } } @@ -145,11 +143,8 @@ func (te *testExecutor) signExecuteCommit(tx payload.Payload, signer acm.Address if err != nil { return err } - appHash, err := te.Commit(nil) - if err != nil { - return err - } - return te.blockchain.CommitBlock(time.Now(), nil, appHash) + _, err = te.Commit(nil, time.Now(), nil) + return err } func makeUsers(n int) []acm.AddressableSigner { @@ -1546,7 +1541,7 @@ func TestSelfDestruct(t *testing.T) { tx := payload.NewCallTxWithSequence(acc0PubKey, addressPtr(acc1), nil, sendingAmount, 1000, 0, acc0.Sequence()+1) // we use cache instead of execTxWithState so we can run the tx twice - exe := NewBatchCommitter(st, newBlockchain(testGenesisDoc).Tip, event.NewNoOpPublisher(), logger) + exe := NewBatchCommitter(st, newBlockchain(testGenesisDoc), event.NewNoOpPublisher(), logger) signAndExecute(t, false, exe, testChainID, tx, privAccounts[0]) // if we do it again, we won't get an error, but the self-destruct @@ -1555,7 +1550,8 @@ func TestSelfDestruct(t *testing.T) { signAndExecute(t, false, exe, testChainID, tx, privAccounts[0]) // commit the block - exe.Commit(nil) + _, err = exe.Commit([]byte("Blocky McHash"), time.Now(), nil) + require.NoError(t, err) // acc2 should receive the sent funds and the contracts balance newAcc2 := getAccount(st, acc2.Address()) @@ -1585,12 +1581,12 @@ func signAndExecute(t *testing.T, shouldFail bool, exe BatchExecutor, chainID st } func execTxWithStateAndBlockchain(state *State, blockchain *bcm.Blockchain, txEnv *txs.Envelope) error { - exe := newExecutor("execTxWithStateAndBlockchainCache", true, state, blockchain.Tip, + exe := newExecutor("execTxWithStateAndBlockchainCache", true, state, blockchain, event.NewNoOpPublisher(), logger) if _, err := exe.Execute(txEnv); err != nil { return err } else { - _, err := exe.Commit(nil) + _, err = exe.Commit([]byte("Blocky McHash"), time.Now(), nil) if err != nil { return err } @@ -1660,7 +1656,7 @@ func execTxWaitAccountCall(t *testing.T, exe *testExecutor, txEnv *txs.Envelope, if err != nil { return nil, err } - _, err = exe.Commit(nil) + _, err = exe.Commit([]byte("Blocky McHash"), time.Now(), nil) require.NoError(t, err) err = exe.blockchain.CommitBlock(time.Time{}, nil, nil) require.NoError(t, err) diff --git a/execution/simulated_call.go b/execution/simulated_call.go index fc7156a4..d921b5e4 100644 --- a/execution/simulated_call.go +++ b/execution/simulated_call.go @@ -9,9 +9,9 @@ import ( "github.com/hyperledger/burrow/binary" "github.com/hyperledger/burrow/blockchain" "github.com/hyperledger/burrow/crypto" + "github.com/hyperledger/burrow/execution/contexts" "github.com/hyperledger/burrow/execution/evm" "github.com/hyperledger/burrow/execution/exec" - "github.com/hyperledger/burrow/execution/executors" "github.com/hyperledger/burrow/logging" ) @@ -70,6 +70,6 @@ func vmParams(tip blockchain.TipInfo) evm.Params { BlockHeight: tip.LastBlockHeight(), BlockHash: binary.LeftPadWord256(tip.LastBlockHash()), BlockTime: tip.LastBlockTime().Unix(), - GasLimit: executors.GasLimit, + GasLimit: contexts.GasLimit, } } diff --git a/execution/transactor_test.go b/execution/transactor_test.go index f8c63895..0d6df572 100644 --- a/execution/transactor_test.go +++ b/execution/transactor_test.go @@ -38,7 +38,7 @@ import ( func TestTransactor_BroadcastTxSync(t *testing.T) { chainID := "TestChain" - tip := blockchain.NewTip(chainID, time.Time{}, []byte("genesis")) + tip := blockchain.NewTip(chainID, time.Time{}, []byte("genesis"), blockchain.NewValidators()) logger := logging.NewNoopLogger() evc := event.NewEmitter(logger) txCodec := txs.NewAminoCodec() diff --git a/genesis/deterministic_genesis.go b/genesis/deterministic_genesis.go index 6c4e6a20..15708c9e 100644 --- a/genesis/deterministic_genesis.go +++ b/genesis/deterministic_genesis.go @@ -59,7 +59,7 @@ func (dg *deterministicGenesis) GenesisDoc(numAccounts int, randBalance bool, mi } return &GenesisDoc{ ChainName: "TestChain", - GenesisTime: time.Unix(1506172037, 0), + GenesisTime: time.Unix(1506172037, 0).UTC(), Accounts: accounts, Validators: validators, }, privAccounts, privValidators diff --git a/genesis/genesis.go b/genesis/genesis.go index 6d91fd47..c98607e1 100644 --- a/genesis/genesis.go +++ b/genesis/genesis.go @@ -66,10 +66,10 @@ type GenesisDoc struct { Validators []Validator } -// JSONBytes returns the JSON (not-yet) canonical bytes for a given -// GenesisDoc or an error. +// JSONBytes returns the JSON canonical bytes for a given GenesisDoc or an error. func (genesisDoc *GenesisDoc) JSONBytes() ([]byte, error) { - // TODO: write JSON in canonical order + // Just in case + genesisDoc.GenesisTime = genesisDoc.GenesisTime.UTC() return json.MarshalIndent(genesisDoc, "", "\t") } diff --git a/genesis/spec/genesis_spec.go b/genesis/spec/genesis_spec.go index cb4fb76c..de96d9a7 100644 --- a/genesis/spec/genesis_spec.go +++ b/genesis/spec/genesis_spec.go @@ -75,7 +75,7 @@ func (gs *GenesisSpec) GenesisDoc(keyClient keys.KeyClient, generateNodeKeys boo } for i, templateAccount := range templateAccounts { - account, err := templateAccount.Account(keyClient, i) + account, err := templateAccount.GenesisAccount(keyClient, i) if err != nil { return nil, fmt.Errorf("could not create Account from template: %v", err) } diff --git a/genesis/spec/template_account.go b/genesis/spec/template_account.go index 1362ec11..b4ce1fa4 100644 --- a/genesis/spec/template_account.go +++ b/genesis/spec/template_account.go @@ -55,7 +55,7 @@ func (ta TemplateAccount) AccountPermissions() (permission.AccountPermissions, e }, nil } -func (ta TemplateAccount) Account(keyClient keys.KeyClient, index int) (*genesis.Account, error) { +func (ta TemplateAccount) GenesisAccount(keyClient keys.KeyClient, index int) (*genesis.Account, error) { var err error ga := new(genesis.Account) ga.PublicKey, ga.Address, err = ta.RealisePubKeyAndAddress(keyClient) diff --git a/integration/governance/main_test.go b/integration/governance/main_test.go new file mode 100644 index 00000000..684d6a13 --- /dev/null +++ b/integration/governance/main_test.go @@ -0,0 +1,47 @@ +// +build integration + +// Space above here matters +// 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 governance + +import ( + "context" + "os" + "testing" + + "github.com/hyperledger/burrow/core" + "github.com/hyperledger/burrow/integration" + "github.com/hyperledger/burrow/integration/rpctest" +) + +var _ = integration.ClaimPorts() +var inputAddress = rpctest.PrivateAccounts[0].Address() +var testConfig = integration.NewTestConfig(rpctest.GenesisDoc) +var kern *core.Kernel + +// Needs to be in a _test.go file to be picked up +func TestMain(m *testing.M) { + kern = integration.TestKernel(rpctest.PrivateAccounts, testConfig) + err := kern.Boot() + if err != nil { + panic(err) + } + // Sometimes better to not shutdown as logging errors on shutdown may obscure real issue + defer func() { + kern.Shutdown(context.Background()) + }() + os.Exit(m.Run()) +} diff --git a/integration/governance/validators_test.go b/integration/governance/validators_test.go new file mode 100644 index 00000000..07b8008d --- /dev/null +++ b/integration/governance/validators_test.go @@ -0,0 +1,7 @@ +package governance + +import "testing" + +func TestName(t *testing.T) { + +} diff --git a/integration/integration.go b/integration/integration.go index 388cae7d..cda7523b 100644 --- a/integration/integration.go +++ b/integration/integration.go @@ -59,7 +59,7 @@ const startingPortBuckets = 1000 var port = uint32(startingPort) // We use this to wrap tests -func TestWrapper(privateAccounts []*acm.PrivateAccount, testConfig *config.BurrowConfig, runner func(*core.Kernel) int) int { +func TestKernel(privateAccounts []*acm.PrivateAccount, testConfig *config.BurrowConfig) *core.Kernel { fmt.Println("Running with integration TestWrapper (core/integration/integration.go)...") os.RemoveAll(testDir) @@ -101,17 +101,8 @@ func TestWrapper(privateAccounts []*acm.PrivateAccount, testConfig *config.Burro if err != nil { panic(err) } - // Sometimes better to not shutdown as logging errors on shutdown may obscure real issue - defer func() { - kernel.Shutdown(context.Background()) - }() - err = kernel.Boot() - if err != nil { - panic(err) - } - - return runner(kernel) + return kernel } func TestGenesisDoc(addressables []*acm.PrivateAccount) *genesis.GenesisDoc { diff --git a/integration/rpcevents/main_test.go b/integration/rpcevents/main_test.go index 8418fbd7..1a842bd9 100644 --- a/integration/rpcevents/main_test.go +++ b/integration/rpcevents/main_test.go @@ -18,6 +18,7 @@ package rpcevents import ( + "context" "os" "testing" @@ -33,11 +34,14 @@ var kern *core.Kernel // Needs to be in a _test.go file to be picked up func TestMain(m *testing.M) { - returnValue := integration.TestWrapper(rpctest.PrivateAccounts, testConfig, - func(k *core.Kernel) int { - kern = k - return m.Run() - }) - - os.Exit(returnValue) + kern = integration.TestKernel(rpctest.PrivateAccounts, testConfig) + err := kern.Boot() + if err != nil { + panic(err) + } + // Sometimes better to not shutdown as logging errors on shutdown may obscure real issue + defer func() { + kern.Shutdown(context.Background()) + }() + os.Exit(m.Run()) } diff --git a/integration/rpcquery/main_test.go b/integration/rpcquery/main_test.go index 423a22bd..9f13b06b 100644 --- a/integration/rpcquery/main_test.go +++ b/integration/rpcquery/main_test.go @@ -18,6 +18,7 @@ package rpcquery import ( + "context" "os" "testing" @@ -32,11 +33,14 @@ var kern *core.Kernel // Needs to be in a _test.go file to be picked up func TestMain(m *testing.M) { - returnValue := integration.TestWrapper(rpctest.PrivateAccounts, testConfig, - func(k *core.Kernel) int { - kern = k - return m.Run() - }) - - os.Exit(returnValue) + kern = integration.TestKernel(rpctest.PrivateAccounts, testConfig) + err := kern.Boot() + if err != nil { + panic(err) + } + // Sometimes better to not shutdown as logging errors on shutdown may obscure real issue + defer func() { + kern.Shutdown(context.Background()) + }() + os.Exit(m.Run()) } diff --git a/integration/rpctransact/main_test.go b/integration/rpctransact/main_test.go index bda89290..1f773a00 100644 --- a/integration/rpctransact/main_test.go +++ b/integration/rpctransact/main_test.go @@ -18,6 +18,7 @@ package rpctransact import ( + "context" "os" "testing" @@ -32,11 +33,14 @@ var kern *core.Kernel // Needs to be in a _test.go file to be picked up func TestMain(m *testing.M) { - returnValue := integration.TestWrapper(rpctest.PrivateAccounts, testConfig, - func(k *core.Kernel) int { - kern = k - return m.Run() - }) - - os.Exit(returnValue) + kern = integration.TestKernel(rpctest.PrivateAccounts, testConfig) + err := kern.Boot() + if err != nil { + panic(err) + } + // Sometimes better to not shutdown as logging errors on shutdown may obscure real issue + defer func() { + kern.Shutdown(context.Background()) + }() + os.Exit(m.Run()) } diff --git a/integration/tm/main_test.go b/integration/tm/main_test.go index cdee8c09..627fa6d2 100644 --- a/integration/tm/main_test.go +++ b/integration/tm/main_test.go @@ -18,6 +18,7 @@ package tm import ( + "context" "os" "testing" @@ -40,10 +41,14 @@ var clients = map[string]tmClient.RPCClient{ // Needs to be in a _test.go file to be picked up func TestMain(m *testing.M) { - returnValue := integration.TestWrapper(rpctest.PrivateAccounts, testConfig, func(k *core.Kernel) int { - kern = k - return m.Run() - }) - - os.Exit(returnValue) + kern = integration.TestKernel(rpctest.PrivateAccounts, testConfig) + err := kern.Boot() + if err != nil { + panic(err) + } + // Sometimes better to not shutdown as logging errors on shutdown may obscure real issue + defer func() { + kern.Shutdown(context.Background()) + }() + os.Exit(m.Run()) } diff --git a/keys/server.go b/keys/server.go index 5613a7b5..1ba471af 100644 --- a/keys/server.go +++ b/keys/server.go @@ -75,8 +75,8 @@ func (k *KeyStore) Export(ctx context.Context, in *ExportRequest) (*ExportRespon return &ExportResponse{ Address: addrB[:], CurveType: key.CurveType.String(), - Publickey: key.PublicKey.PublicKey[:], - Privatekey: key.PrivateKey.PrivateKey[:], + Publickey: key.PublicKey.Key[:], + Privatekey: key.PrivateKey.Key[:], }, nil } diff --git a/permission/perm_flag.go b/permission/perm_flag.go index 617aa561..2ddece98 100644 --- a/permission/perm_flag.go +++ b/permission/perm_flag.go @@ -76,6 +76,11 @@ const ( // A particular permission type PermFlag uint64 +// Checks if a permission flag is valid (a known base chain or snative permission) +func (pf PermFlag) IsValid() bool { + return pf <= AllPermFlags +} + // Returns the string name of a single bit non-composite PermFlag, or otherwise UnknownString // See BasePermissionsToStringList to generate a string representation of a composite PermFlag func (pf PermFlag) String() string { diff --git a/protobuf/crypto.proto b/protobuf/crypto.proto index 425236f4..9b95283b 100644 --- a/protobuf/crypto.proto +++ b/protobuf/crypto.proto @@ -16,7 +16,7 @@ option (gogoproto.messagename_all) = true; message PublicKey { option (gogoproto.goproto_stringer) = false; uint32 CurveType = 1 [(gogoproto.casttype) = "CurveType"]; - bytes PublicKey = 2; + bytes Key = 2; } message PrivateKey { @@ -25,5 +25,5 @@ message PrivateKey { uint32 CurveType = 1 [(gogoproto.casttype) = "CurveType"]; // Note may need initialisation bytes PublicKey = 2; - bytes PrivateKey = 3; + bytes Key = 3; } diff --git a/protobuf/exec.proto b/protobuf/exec.proto index 6eb93511..58a8b2c9 100644 --- a/protobuf/exec.proto +++ b/protobuf/exec.proto @@ -11,6 +11,7 @@ import "errors.proto"; import "names.proto"; import "txs.proto"; import "permission.proto"; +import "spec.proto"; option (gogoproto.marshaler_all) = true; option (gogoproto.unmarshaler_all) = true; @@ -33,9 +34,6 @@ message BlockHeader { } message TxExecution { - option (gogoproto.sizer) = true; - option (gogoproto.marshaler) = true; - option (gogoproto.unmarshaler) = true; // Transaction type uint32 TxType = 2 [(gogoproto.casttype) = "github.com/hyperledger/burrow/txs/payload.Type"]; // The hash of the transaction that caused this event to be generated @@ -58,9 +56,6 @@ message TxExecution { message Header { option (gogoproto.goproto_stringer) = false; - option (gogoproto.sizer) = true; - option (gogoproto.marshaler) = true; - option (gogoproto.unmarshaler) = true; // Transaction type uint32 TxType = 1 [(gogoproto.casttype) = "github.com/hyperledger/burrow/txs/payload.Type"]; // The hash of the transaction that caused this event to be generated @@ -78,22 +73,17 @@ message Header { } message Event { - option (gogoproto.sizer) = true; - option (gogoproto.marshaler) = true; - option (gogoproto.unmarshaler) = true; option (gogoproto.goproto_stringer) = false; Header Header = 1; InputEvent Input = 2; OutputEvent Output = 3; CallEvent Call = 4; LogEvent Log = 5; + GovernAccountEvent GovernAccount = 6; } // Could structure this further if needed - sum type of various results relevant to different transaction types message Result { - option (gogoproto.sizer) = true; - option (gogoproto.marshaler) = true; - option (gogoproto.unmarshaler) = true; // EVM execution return bytes Return = 1; // Gas used in computation @@ -105,42 +95,31 @@ message Result { } message LogEvent { - option (gogoproto.sizer) = true; - option (gogoproto.marshaler) = true; - option (gogoproto.unmarshaler) = true; bytes Address = 1 [(gogoproto.customtype) = "github.com/hyperledger/burrow/crypto.Address", (gogoproto.nullable) = false]; bytes Data = 2 [(gogoproto.customtype) = "github.com/hyperledger/burrow/binary.HexBytes", (gogoproto.nullable) = false]; repeated bytes Topics = 3 [(gogoproto.customtype) = "github.com/hyperledger/burrow/binary.Word256", (gogoproto.nullable) = false]; } message CallEvent { - option (gogoproto.sizer) = true; - option (gogoproto.marshaler) = true; - option (gogoproto.unmarshaler) = true; CallData CallData = 1; bytes Origin = 2 [(gogoproto.customtype) = "github.com/hyperledger/burrow/crypto.Address", (gogoproto.nullable) = false]; uint64 StackDepth = 3; bytes Return = 4 [(gogoproto.customtype) = "github.com/hyperledger/burrow/binary.HexBytes", (gogoproto.nullable) = false]; } +message GovernAccountEvent { + spec.TemplateAccount AccountUpdate = 1; +} + message InputEvent { - option (gogoproto.sizer) = true; - option (gogoproto.marshaler) = true; - option (gogoproto.unmarshaler) = true; bytes Address = 1 [(gogoproto.customtype) = "github.com/hyperledger/burrow/crypto.Address", (gogoproto.nullable) = false]; } message OutputEvent { - option (gogoproto.sizer) = true; - option (gogoproto.marshaler) = true; - option (gogoproto.unmarshaler) = true; bytes Address = 1 [(gogoproto.customtype) = "github.com/hyperledger/burrow/crypto.Address", (gogoproto.nullable) = false]; } message CallData { - option (gogoproto.sizer) = true; - option (gogoproto.marshaler) = true; - option (gogoproto.unmarshaler) = true; bytes Caller = 1 [(gogoproto.customtype) = "github.com/hyperledger/burrow/crypto.Address", (gogoproto.nullable) = false]; bytes Callee = 2 [(gogoproto.customtype) = "github.com/hyperledger/burrow/crypto.Address", (gogoproto.nullable) = false]; bytes Data = 3 [(gogoproto.customtype) = "github.com/hyperledger/burrow/binary.HexBytes", (gogoproto.nullable) = false]; diff --git a/rpc/service.go b/rpc/service.go index 098dd150..42ebc1cf 100644 --- a/rpc/service.go +++ b/rpc/service.go @@ -17,6 +17,7 @@ package rpc import ( "encoding/json" "fmt" + "math/big" "time" "github.com/hyperledger/burrow/acm" @@ -323,11 +324,11 @@ func (s *Service) ListBlocks(minHeight, maxHeight uint64) (*ResultListBlocks, er func (s *Service) ListValidators() (*ResultListValidators, error) { concreteValidators := make([]*acm.ConcreteValidator, 0, s.blockchain.NumValidators()) - s.blockchain.IterateValidators(func(publicKey crypto.PublicKey, power uint64) (stop bool) { + s.blockchain.IterateValidators(func(id crypto.Addressable, power *big.Int) (stop bool) { concreteValidators = append(concreteValidators, &acm.ConcreteValidator{ - Address: publicKey.Address(), - PublicKey: publicKey, - Power: power, + Address: id.Address(), + PublicKey: id.PublicKey(), + Power: power.Uint64(), }) return }) diff --git a/txs/payload/governance_tx.go b/txs/payload/governance_tx.go index 046f9129..5f8bfdeb 100644 --- a/txs/payload/governance_tx.go +++ b/txs/payload/governance_tx.go @@ -2,35 +2,8 @@ package payload import ( "fmt" - - "github.com/hyperledger/burrow/acm/state" - "github.com/hyperledger/burrow/crypto" - "github.com/hyperledger/burrow/genesis/spec" ) -func NewGovTx(st state.AccountGetter, from crypto.Address, accounts ...*spec.TemplateAccount) (*GovernanceTx, error) { - acc, err := st.GetAccount(from) - if err != nil { - return nil, err - } - if acc == nil { - return nil, fmt.Errorf("could not get account %v", from) - } - - sequence := acc.Sequence() + 1 - return NewGovTxWithSequence(from, sequence, accounts), nil -} - -func NewGovTxWithSequence(from crypto.Address, sequence uint64, accounts []*spec.TemplateAccount) *GovernanceTx { - return &GovernanceTx{ - Inputs: []*TxInput{{ - Address: from, - Sequence: sequence, - }}, - AccountUpdates: accounts, - } -} - func (tx *GovernanceTx) Type() Type { return TypeGovernance } -- GitLab