diff --git a/chain/chain.go b/chain/chain.go index 8cb67bea3..392e74992 100644 --- a/chain/chain.go +++ b/chain/chain.go @@ -74,6 +74,11 @@ type chainTask interface { Wait() error } +type simpleTask interface { + chainTask + Run() error +} + type singleChain struct { wallet module.Wallet @@ -502,6 +507,9 @@ func (c *singleChain) releaseManagers() { } func (c *singleChain) _runTask(task chainTask, wait bool) error { + if st, ok := task.(simpleTask) ; ok { + return st.Run() + } if err := c._setStartingTask(task); err != nil { return err } diff --git a/chain/taskpause.go b/chain/taskpause.go new file mode 100644 index 000000000..4e69dd721 --- /dev/null +++ b/chain/taskpause.go @@ -0,0 +1,131 @@ +/* + * Copyright 2023 Parameta Corp + * + * 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 chain + +import ( + "encoding/json" + + "github.com/icon-project/goloop/common/errors" +) + +type taskPause struct { + chain *singleChain + result resultStore +} + +var pauseStates = map[State]string{ + Starting: "pausing", + Started: "paused", + Stopping: "stopping paused", + Failed: "fail to pause", +} + +func (t *taskPause) String() string { + return "Pause" +} + +func (t *taskPause) DetailOf(s State) string { + if name, ok := pauseStates[s]; ok { + return name + } else { + return s.String() + } +} + +func (t *taskPause) Start() error { + if err := t.chain.prepareManagers(); err != nil { + t.result.SetValue(err) + return err + } + if err := t._start(t.chain); err != nil { + t.chain.releaseManagers() + t.result.SetValue(err) + return err + } + return nil +} + +func (t *taskPause) _start(c *singleChain) error { + c.sm.Start() + //if err := c.cs.Start(); err != nil { + // return err + //} + c.srv.SetChain(c.cfg.Channel, c) + if err := c.nm.Start(); err != nil { + return err + } + return nil +} + +func (t *taskPause) Stop() { + t.chain.srv.RemoveChain(t.chain.cfg.Channel) + t.chain.releaseManagers() + t.result.SetValue(errors.ErrInterrupted) +} + +func (t *taskPause) Wait() error { + return t.result.Wait() +} + +func newTaskPause(chain *singleChain, params json.RawMessage) (chainTask, error) { + return &taskPause{ + chain: chain, + }, nil +} + +type taskResume struct { + chain *singleChain +} + +func (t *taskResume) String() string { + panic("invalid usage") +} + +func (t *taskResume) DetailOf(s State) string { + panic("invalid usage") +} + +func (t *taskResume) Start() error { + panic("invalid usage") +} + +func (t *taskResume) Stop() { + panic("invalid usage") +} + +func (t *taskResume) Wait() error { + panic("invalid usage") +} + +func (t *taskResume) Run() error { + if _, ok := t.chain.task.(*taskPause) ; ok { + return t.chain.cs.Start() + } else { + return errors.InvalidStateError.New("Not in PAUSED state") + } +} + +func newTaskResume(c *singleChain, params json.RawMessage) (chainTask, error) { + return &taskResume{ + chain: c, + }, nil +} + +func init() { + registerTaskFactory("pause", newTaskPause) + registerTaskFactory("resume", newTaskResume) +} diff --git a/cmd/cli/rpc.go b/cmd/cli/rpc.go index 167ecff17..3e0831315 100644 --- a/cmd/cli/rpc.go +++ b/cmd/cli/rpc.go @@ -332,7 +332,7 @@ func NewRpcCmd(parentCmd *cobra.Command, parentVc *viper.Viper) (*cobra.Command, callFlags.Int("height", -1, "BlockHeight") callFlags.String("method", "", "Name of the function to invoke in SCORE, if '--raw' used, will overwrite") - callFlags.StringToString("params", nil, + callFlags.String("params", "", "raw json string or '@' or '-' for stdin for parameter JSON. it overrides raw one ") callFlags.StringToString("param", nil, "key=value, Function parameters, if '--raw' used, will overwrite") diff --git a/cmd/txgen/main.go b/cmd/txgen/main.go index 2af0253fd..398956a2a 100644 --- a/cmd/txgen/main.go +++ b/cmd/txgen/main.go @@ -6,13 +6,15 @@ import ( "log" "os" - "github.com/icon-project/goloop/common/wallet" "github.com/spf13/cobra" + + "github.com/icon-project/goloop/common/wallet" ) func main() { var keyStoreFile string var keyStorePass string + var keyStoreSecret string var scorePath string var tps int var concurrent int @@ -31,6 +33,7 @@ func main() { flags := cmd.PersistentFlags() flags.StringVarP(&keyStoreFile, "keystore", "k", "", "File path to keystore of base account (like GOD)") flags.StringVarP(&keyStorePass, "password", "p", "gochain", "Password for the keystore") + flags.StringVar(&keyStoreSecret, "secret", "", "Secret file containing password for the keystore") flags.IntVarP(&tps, "tps", "t", 1000, "Max transaction per a second") flags.IntVarP(&concurrent, "concurrent", "c", 2, "Number of subroutines (threads)") flags.IntVarP(&walletCount, "wallets", "w", 1000, "Number of temporal wallets") @@ -57,8 +60,16 @@ func main() { if err != nil { log.Panicf("Fail to read KeyStore file=%s err=%+v", keyStoreFile, err) } + var pass []byte; + if len(keyStoreSecret)>0 { + if pass, err = os.ReadFile(keyStoreSecret); err != nil { + log.Panicf("Fail to read secret for keyStore file=%s", keyStoreSecret); + } + } else { + pass = []byte(keyStorePass); + } - godWallet, err := wallet.NewFromKeyStore(ks, []byte(keyStorePass)) + godWallet, err := wallet.NewFromKeyStore(ks, pass) if err != nil { log.Panicf("Fail to decrypt KeyStore err=%+v", err) } diff --git a/common/codec/bytes.go b/common/codec/bytes.go index 01dae7b50..575c8337d 100644 --- a/common/codec/bytes.go +++ b/common/codec/bytes.go @@ -3,41 +3,91 @@ package codec import ( "bytes" "io" + "sync" "github.com/icon-project/goloop/common/log" ) +type bytesEncoder struct { + Encoder + buffer *bytes.Buffer +} + +func (e *bytesEncoder) Reset() { + e.buffer.Reset() +} + +func (e *bytesEncoder) Bytes() []byte { + return []byte(e.buffer.String()) +} + +type bytesDecoder struct { + Decoder + buffer *bytes.Buffer +} + +func (d *bytesDecoder) Reset(bs []byte) { + d.buffer.Reset() + d.buffer.Write(bs) + d.Decoder.SetMaxBytes(len(bs)) +} + +func (d *bytesDecoder) Bytes() []byte { + return []byte(d.buffer.String()) +} + type bytesWrapper struct { codecImpl + encoders sync.Pool + decoders sync.Pool } -func (c bytesWrapper) Marshal(w io.Writer, v interface{}) error { +func (c *bytesWrapper) Marshal(w io.Writer, v interface{}) error { return c.NewEncoder(w).Encode(v) } -func (c bytesWrapper) Unmarshal(r io.Reader, v interface{}) error { +func (c *bytesWrapper) Unmarshal(r io.Reader, v interface{}) error { return c.NewDecoder(r).Decode(v) } -func (c bytesWrapper) MarshalToBytes(v interface{}) ([]byte, error) { - buf := bytes.NewBuffer(nil) - if err := c.NewEncoder(buf).Encode(v); err != nil { +func bytesDup(bs []byte) []byte { + sz := len(bs) + if sz != 0 { + nbs := make([]byte,len(bs)) + copy(nbs, bs) + return nbs + } else { + return []byte{} + } +} + +func (c *bytesWrapper) MarshalToBytes(v interface{}) ([]byte, error) { + be := c.encoders.Get().(*bytesEncoder) + + be.Reset() + if err := be.Encode(v) ; err != nil { return nil, err } - return buf.Bytes(), nil + remainder := bytesDup(be.Bytes()) + + c.encoders.Put(be) + return remainder, nil } -func (c bytesWrapper) UnmarshalFromBytes(b []byte, v interface{}) ([]byte, error) { - buf := bytes.NewBuffer(b) - dec := c.NewDecoder(buf) - dec.SetMaxBytes(len(b)) - if err := dec.Decode(v); err != nil { +func (c *bytesWrapper) UnmarshalFromBytes(b []byte, v interface{}) ([]byte, error) { + bd := c.decoders.Get().(*bytesDecoder) + + bd.Reset(b) + if err := bd.Decode(v); err != nil { return nil, err } - return buf.Bytes(), nil + bs := bytesDup(bd.Bytes()) + + c.decoders.Put(bd) + return bs, nil } -func (c bytesWrapper) MustMarshalToBytes(v interface{}) []byte { +func (c *bytesWrapper) MustMarshalToBytes(v interface{}) []byte { bs, err := c.MarshalToBytes(v) if err != nil { log.Panicf("MustMarshalToBytes() fails for object=%T err=%+v", v, err) @@ -47,7 +97,7 @@ func (c bytesWrapper) MustMarshalToBytes(v interface{}) []byte { } } -func (c bytesWrapper) MustUnmarshalFromBytes(b []byte, v interface{}) []byte { +func (c *bytesWrapper) MustUnmarshalFromBytes(b []byte, v interface{}) []byte { bs, err := c.UnmarshalFromBytes(b, v) if err != nil { log.Panicf("MustUnmarshalFromBytes() fails for bytes=% x buffer=%T err=%+v", b, v, err) @@ -66,9 +116,33 @@ func (w bytesWriter) Write(bs []byte) (int, error) { return len(bs), nil } -func (c bytesWrapper) NewEncoderBytes(b *[]byte) EncodeAndCloser { +func (c *bytesWrapper) NewEncoderBytes(b *[]byte) EncodeAndCloser { if len(*b) > 0 { *b = (*b)[:0] } return c.codecImpl.NewEncoder(&bytesWriter{b}) } + +func bytesWrapperFrom(codec codecImpl) *bytesWrapper { + return &bytesWrapper{ + codecImpl: codec, + encoders: sync.Pool{ + New: func() interface{} { + buffer := bytes.NewBuffer(nil) + return &bytesEncoder{ + Encoder: codec.NewEncoder(buffer), + buffer: buffer, + } + }, + }, + decoders: sync.Pool{ + New: func() interface{} { + buffer := bytes.NewBuffer(nil) + return &bytesDecoder{ + Decoder: codec.NewDecoder(buffer), + buffer: buffer, + } + }, + }, + } +} \ No newline at end of file diff --git a/common/codec/codec.go b/common/codec/codec.go index a74850480..ac1810e20 100644 --- a/common/codec/codec.go +++ b/common/codec/codec.go @@ -168,52 +168,35 @@ func (e *encoderImpl) encodeMap() (*encoderImpl, error) { return e.child, nil } -var bigIntPtrType = reflect.TypeOf((*big.Int)(nil)) - -var writeSelferType = reflect.TypeOf((*WriteSelfer)(nil)).Elem() -var encodeSelferType = reflect.TypeOf((*EncodeSelfer)(nil)).Elem() -var binaryMarshaler = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem() -var codecMarshaler = reflect.TypeOf((*Marshaler)(nil)).Elem() - func (e *encoderImpl) tryCustom(v reflect.Value) (bool, error) { - if v.Type().Implements(writeSelferType) { - if i, ok := v.Interface().(WriteSelfer); ok { - return true, i.RLPWriteSelf(e.real) - } - } - if v.Type().Implements(encodeSelferType) { - if i, ok := v.Interface().(EncodeSelfer); ok { - if err := i.RLPEncodeSelf(e); err == nil { + if v.CanInterface() { + switch value := v.Interface().(type) { + case EncodeSelfer: + if err := value.RLPEncodeSelf(e); err == nil { return true, e.flush() } else { return true, err } - } - } - if v.Type().Implements(binaryMarshaler) { - if i, ok := v.Interface().(encoding.BinaryMarshaler); ok { - b, err := i.MarshalBinary() + case WriteSelfer: + return true, value.RLPWriteSelf(e.real) + case encoding.BinaryMarshaler: + b, err := value.MarshalBinary() if err != nil { return true, err } return true, e.real.WriteBytes(b) - } - } - if v.Type().Implements(codecMarshaler) { - if i, ok := v.Interface().(Marshaler); ok { - b, err := i.MarshalRLP() + case Marshaler: + b, err := value.MarshalRLP() if err != nil { return true, err } return true, e.real.WriteRaw(b) - } - } - if v.Type().ConvertibleTo(bigIntPtrType) { - bi := v.Interface().(*big.Int) - if bi != nil { - return true, e.Encode(intconv.BigIntToBytes(bi)) - } else { - return true, e.Encode(nil) + case *big.Int: + if value != nil { + return true, e.Encode(intconv.BigIntToBytes(value)) + } else { + return true, e.Encode(nil) + } } } return false, nil @@ -515,48 +498,39 @@ func (d *decoderImpl) SetMaxBytes(sz int) bool { return false } -var readSelferType = reflect.TypeOf((*ReadSelfer)(nil)).Elem() -var rlpDecodeSelferType = reflect.TypeOf((*DecodeSelfer)(nil)).Elem() -var binaryUnmarshaler = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem() -var codecUnmarshaler = reflect.TypeOf((*Unmarshaler)(nil)).Elem() - -func (d *decoderImpl) tryCustom(v reflect.Value) (bool, error) { - if v.Type().Implements(readSelferType) { - return true, v.Interface().(ReadSelfer).RLPReadSelf(d.real) - } - if v.Type().Implements(rlpDecodeSelferType) { - if err := v.Interface().(DecodeSelfer).RLPDecodeSelf(d); err == nil { - return true, d.flush() - } else { - return true, err - } - } - if v.Type().Implements(binaryUnmarshaler) { - u := v.Interface().(encoding.BinaryUnmarshaler) - b, err := d.real.ReadBytes() - if err != nil { - return true, err - } - return true, u.UnmarshalBinary(b) - } - if v.Type().Implements(codecUnmarshaler) { - u := v.Interface().(Unmarshaler) - b, err := d.real.ReadRaw() - if err != nil { - return true, err - } - return true, u.UnmarshalRLP(b) - } - if v.Type().ConvertibleTo(bigIntPtrType) { - bi := v.Interface().(*big.Int) - b, err := d.real.ReadBytes() - if err != nil { - return true, err - } - if v := intconv.BigIntSetBytes(bi, b); v != nil { - return true, nil - } else { - return true, ErrInvalidFormat +func (d *decoderImpl) tryCustom(v reflect.Value) (consume bool, err error) { + if v.CanInterface() { + switch value := v.Interface().(type) { + case DecodeSelfer: + if err := value.RLPDecodeSelf(d); err == nil { + return true, d.flush() + } else { + return true, err + } + case ReadSelfer: + return true, value.RLPReadSelf(d.real) + case encoding.BinaryUnmarshaler: + b, err := d.real.ReadBytes() + if err != nil { + return true, err + } + return true, value.UnmarshalBinary(b) + case Unmarshaler: + b, err := d.real.ReadRaw() + if err != nil { + return true, err + } + return true, value.UnmarshalRLP(b) + case *big.Int: + b, err := d.real.ReadBytes() + if err != nil { + return true, err + } + if v := intconv.BigIntSetBytes(value, b); v != nil { + return true, nil + } else { + return true, ErrInvalidFormat + } } } return false, nil diff --git a/common/codec/msgpack.go b/common/codec/msgpack.go index e5f539e94..8f338a62c 100644 --- a/common/codec/msgpack.go +++ b/common/codec/msgpack.go @@ -12,7 +12,7 @@ import ( ) var mpCodecObject mpCodec -var MP = bytesWrapper{&mpCodecObject} +var MP = bytesWrapperFrom(&mpCodecObject) type mpCodec struct { } diff --git a/common/codec/rlp.go b/common/codec/rlp.go index 555f9d470..061695645 100644 --- a/common/codec/rlp.go +++ b/common/codec/rlp.go @@ -11,7 +11,7 @@ import ( ) var rlpCodecObject rlpCodec -var RLP = bytesWrapper{&rlpCodecObject} +var RLP = bytesWrapperFrom(&rlpCodecObject) // MaxSizeForBytes is size limit for bytes buffer. // msgpack decoder already has limit to 1 MB diff --git a/common/db/databasewriter.go b/common/db/databasewriter.go new file mode 100644 index 000000000..6bc3baae0 --- /dev/null +++ b/common/db/databasewriter.go @@ -0,0 +1,91 @@ +/* + * Copyright 2023 ICON Foundation + * + * 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 db + +import ( + "sync" + "time" + + "github.com/icon-project/goloop/common/log" +) + +type Flusher interface { + Flush() error +} + +type Writer interface { + Database() Database + Add(item Flusher) + Prepare() + Flush() error +} + +type writer struct { + layerDB LayerDB + waiters sync.WaitGroup + items []Flusher + results []error +} + +func (w *writer) Database() Database { + return w.layerDB +} + +func (w *writer) Add(r Flusher) { + w.items = append(w.items, r) + w.waiters.Add(1) +} + +func (w *writer) doFlush(idx int) { + w.results[idx] = w.items[idx].Flush() + w.waiters.Done() +} + +func (w *writer) Prepare() { + if w.results != nil { + return + } + log.Debugf("db.Writer[%p].Prepare()", w) + w.results = make([]error, len(w.items)) + for i := 0 ; i= 0; i-- { child := n.children[i] @@ -269,12 +279,14 @@ func (n *branch) traverse(m *mpt, k string, v nodeScheduler) (string, trie.Objec return "", nil, err } if child != nchild { + lock.Migrate() n.children[i] = nchild } } if n.value != nil { value, changed, err := m.getObject(n.value) if changed { + lock.Migrate() n.value = value } if err == nil { @@ -285,8 +297,8 @@ func (n *branch) traverse(m *mpt, k string, v nodeScheduler) (string, trie.Objec } func (n *branch) getProof(m *mpt, keys []byte, proofs [][]byte) (nn node, proof [][]byte, err error) { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() if n.state < stateHashed { return n, nil, fmt.Errorf("IllegaState %s", n.toString()) @@ -303,18 +315,20 @@ func (n *branch) getProof(m *mpt, keys []byte, proofs [][]byte) (nn node, proof } nchild, proofs, err := child.getProof(m, keys[1:], proofs) if nchild != child { + lock.Migrate() n.children[keys[0]] = nchild } return n, proofs, err } func (n *branch) prove(m *mpt, keys []byte, proof [][]byte) (nn node, obj trie.Object, err error) { - n.mutex.Lock() + lock := n.rlock() defer func() { if err == nil && n.state == stateFlushed { + lock.Migrate() n.state = stateWritten } - n.mutex.Unlock() + defer lock.Unlock() }() if n.hashValue != nil { @@ -331,6 +345,7 @@ func (n *branch) prove(m *mpt, keys []byte, proof [][]byte) (nn node, obj trie.O return n, nil, err } if changed { + lock.Migrate() n.value = value } } @@ -343,6 +358,7 @@ func (n *branch) prove(m *mpt, keys []byte, proof [][]byte) (nn node, obj trie.O } nchild, obj, err := child.prove(m, keys[1:], proof) if nchild != child { + lock.Migrate() n.children[keys[0]] = nchild } return n, obj, err @@ -371,10 +387,11 @@ func (n *branch) resolve(m *mpt, bd merkle.Builder) error { } func (n *branch) compact() node { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() if n.state < stateFlushed { + lock.Migrate() for i := range n.children { node := n.children[i] if node != nil { diff --git a/common/trie/ompt/extension.go b/common/trie/ompt/extension.go index 3f86063e3..83a79a2fb 100644 --- a/common/trie/ompt/extension.go +++ b/common/trie/ompt/extension.go @@ -56,26 +56,31 @@ func (n *extension) RLPListSize() int { } func (n *extension) RLPListEncode(e RLPEncoder) error { - e.RLPEncode(encodeKeys(0x00, n.keys)) + if err := e.RLPEncode(encodeKeys(0x00, n.keys)); err != nil { + return err + } e.RLPWrite(n.next.getLink(false)) return nil } func (n *extension) freeze() { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() if n.state != stateDirty { return } if n.next != nil { n.next.freeze() } - n.state = stateFrozen + lock.Migrate() + if n.state == stateDirty { + n.state = stateFrozen + } } func (n *extension) flush(m *mpt, nibs []byte) error { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() if n.state == stateFlushed { return nil } @@ -85,11 +90,14 @@ func (n *extension) flush(m *mpt, nibs []byte) error { if err := n.nodeBase.flushBaseInLock(m, nil); err != nil { return err } + lock.Migrate() + n.state = stateFlushed return nil } -func (n *extension) getChanged(keys []byte, next node) *extension { +func (n *extension) getChanged(lock *AutoRWUnlock, keys []byte, next node) *extension { if n.state == stateDirty { + lock.Migrate() n.keys = keys n.next = next return n @@ -98,12 +106,12 @@ func (n *extension) getChanged(keys []byte, next node) *extension { } func (n *extension) set(m *mpt, nibs []byte, depth int, o trie.Object) (node, bool, trie.Object, error) { + lock := n.rlock() + defer lock.Unlock() + keys := nibs[depth:] cnt, _ := compareKeys(keys, n.keys) - n.mutex.Lock() - defer n.mutex.Unlock() - switch { case cnt == 0: nb := &branch{} @@ -116,7 +124,7 @@ func (n *extension) set(m *mpt, nibs []byte, depth int, o trie.Object) (node, bo nb.children[n.keys[0]] = n.next } else { idx := n.keys[0] - nb.children[idx] = n.getChanged(n.keys[1:], n.next) + nb.children[idx] = n.getChanged(&lock, n.keys[1:], n.next) } return nb, true, nil, nil case cnt < len(n.keys): @@ -132,13 +140,14 @@ func (n *extension) set(m *mpt, nibs []byte, depth int, o trie.Object) (node, bo } else { br.children[keys[cnt]] = &leaf{keys: clone(keys[cnt+1:]), value: o} } - return n.getChanged(n.keys[:cnt], br), true, nil, nil + return n.getChanged(&lock, n.keys[:cnt], br), true, nil, nil default: next, dirty, old, err := n.next.set(m, nibs, depth+cnt, o) if dirty { - return n.getChanged(n.keys, next), true, old, err + return n.getChanged(&lock, n.keys, next), true, old, err } else { if n.next != next { + lock.Migrate() n.next = next } } @@ -147,20 +156,20 @@ func (n *extension) set(m *mpt, nibs []byte, depth int, o trie.Object) (node, bo } func (n *extension) getKeyPrepended(k []byte) *extension { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() nk := make([]byte, len(k)+len(n.keys)) copy(nk, k) copy(nk[len(k):], n.keys) - return n.getChanged(nk, n.next) + return n.getChanged(&lock, nk, n.next) } func (n *extension) delete(m *mpt, nibs []byte, depth int) (node, bool, trie.Object, error) { keys := nibs[depth:] - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() cnt, _ := compareKeys(keys, n.keys) if cnt < len(n.keys) { @@ -177,9 +186,10 @@ func (n *extension) delete(m *mpt, nibs []byte, depth int) (node, bool, trie.Obj case *leaf: return nn.getKeyPrepended(n.keys), true, old, err } - return n.getChanged(n.keys, next), true, old, err + return n.getChanged(&lock, n.keys, next), true, old, err } else { if n.next != next { + lock.Migrate() n.next = next } } @@ -188,14 +198,17 @@ func (n *extension) delete(m *mpt, nibs []byte, depth int) (node, bool, trie.Obj func (n *extension) get(m *mpt, nibs []byte, depth int) (node, trie.Object, error) { keys := nibs[depth:] + lock := n.rlock() + defer lock.Unlock() cnt, _ := compareKeys(keys, n.keys) if cnt < len(n.keys) { return n, nil, nil } - n.mutex.Lock() - defer n.mutex.Unlock() - next, obj, err := n.next.get(m, nibs, depth+cnt) - n.next = next + nv, obj, err := n.next.get(m, nibs, depth+cnt) + if nv != n.next { + lock.Migrate() + n.next = nv + } return n, obj, err } @@ -204,14 +217,15 @@ func (n *extension) realize(m *mpt) (node, error) { } func (n *extension) traverse(m *mpt, k string, v nodeScheduler) (string, trie.Object, error) { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() next, err := v(k+string(n.keys), n.next) if err != nil { return "", nil, err } if next != n.next { + lock.Migrate() n.next = next } return "", nil, nil @@ -274,10 +288,11 @@ func (n *extension) resolve(m *mpt, bd merkle.Builder) error { } func (n *extension) compact() node { - n.mutex.Lock() - defer n.mutex.Unlock() + lock :=n.rlock() + defer lock.Unlock() if n.state < stateFlushed { + lock.Migrate() n.next = n.next.compact() return n } diff --git a/common/trie/ompt/leaf.go b/common/trie/ompt/leaf.go index f6b0d0e7e..2544a7403 100644 --- a/common/trie/ompt/leaf.go +++ b/common/trie/ompt/leaf.go @@ -53,17 +53,21 @@ func (n *leaf) dump() { } func (n *leaf) freeze() { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() if n.state != stateDirty { return } - n.state = stateFrozen + lock.Migrate() + if n.state == stateDirty { + n.state = stateFrozen + } } func (n *leaf) flush(m *mpt, nibs []byte) error { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() + if n.state == stateFlushed { return nil } @@ -76,6 +80,8 @@ func (n *leaf) flush(m *mpt, nibs []byte) error { if err := n.nodeBase.flushBaseInLock(m, nil); err != nil { return err } + lock.Migrate() + n.state = stateFlushed return nil } @@ -84,13 +90,18 @@ func (n *leaf) RLPListSize() int { } func (n *leaf) RLPListEncode(e RLPEncoder) error { - e.RLPEncode(encodeKeys(0x20, n.keys)) - e.RLPEncode(n.value.Bytes()) + if err := e.RLPEncode(encodeKeys(0x20, n.keys)); err != nil { + return err + } + if err := e.RLPEncode(n.value.Bytes()); err != nil { + return err + } return nil } -func (n *leaf) getChanged(keys []byte, o trie.Object) *leaf { +func (n *leaf) getChanged(lock *AutoRWUnlock, keys []byte, o trie.Object) *leaf { if n.state == stateDirty { + lock.Migrate() n.keys = keys n.value = o return n @@ -102,8 +113,8 @@ func (n *leaf) set(m *mpt, nibs []byte, depth int, o trie.Object) (node, bool, t keys := nibs[depth:] cnt, match := compareKeys(keys, n.keys) - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() switch { case cnt == 0 && !match: @@ -120,7 +131,7 @@ func (n *leaf) set(m *mpt, nibs []byte, depth int, o trie.Object) (node, bool, t br.value = n.value } else { idx := n.keys[0] - br.children[idx] = n.getChanged(n.keys[1:], n.value) + br.children[idx] = n.getChanged(&lock, n.keys[1:], n.value) } return br, true, nil, nil case cnt < len(n.keys): @@ -132,7 +143,7 @@ func (n *leaf) set(m *mpt, nibs []byte, depth int, o trie.Object) (node, bool, t br.children[keys[cnt]] = &leaf{keys: clone(keys[cnt+1:]), value: o} } idx := n.keys[cnt] - br.children[idx] = n.getChanged(n.keys[cnt+1:], n.value) + br.children[idx] = n.getChanged(&lock, n.keys[cnt+1:], n.value) return ext, true, nil, nil case cnt < len(keys): br := &branch{} @@ -145,18 +156,18 @@ func (n *leaf) set(m *mpt, nibs []byte, depth int, o trie.Object) (node, bool, t if n.value.Equal(o) { return n, false, old, nil } - return n.getChanged(n.keys, o), true, old, nil + return n.getChanged(&lock, n.keys, o), true, old, nil } } func (n *leaf) getKeyPrepended(k []byte) *leaf { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() nk := make([]byte, len(k)+len(n.keys)) copy(nk, k) copy(nk[len(k):], n.keys) - return n.getChanged(nk, n.value) + return n.getChanged(&lock, nk, n.value) } func (n *leaf) delete(m *mpt, nibs []byte, depth int) (node, bool, trie.Object, error) { @@ -168,14 +179,16 @@ func (n *leaf) delete(m *mpt, nibs []byte, depth int) (node, bool, trie.Object, } func (n *leaf) get(m *mpt, nibs []byte, depth int) (node, trie.Object, error) { + lock := n.rlock() + defer lock.Unlock() + _, match := compareKeys(nibs[depth:], n.keys) if !match { return n, nil, nil } - n.mutex.Lock() - defer n.mutex.Unlock() nv, changed, err := m.getObject(n.value) if changed { + lock.Migrate() n.value = nv } return n, nv, err @@ -186,11 +199,12 @@ func (n *leaf) realize(m *mpt) (node, error) { } func (n *leaf) traverse(m *mpt, k string, v nodeScheduler) (string, trie.Object, error) { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() value, changed, err := m.getObject(n.value) if changed { + lock.Migrate() n.value = value } if err != nil { @@ -200,8 +214,8 @@ func (n *leaf) traverse(m *mpt, k string, v nodeScheduler) (string, trie.Object, } func (n *leaf) getProof(m *mpt, keys []byte, items [][]byte) (node, [][]byte, error) { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() if n.state < stateHashed { return n, nil, fmt.Errorf("IllegaState %s", n.toString()) @@ -216,8 +230,8 @@ func (n *leaf) getProof(m *mpt, keys []byte, items [][]byte) (node, [][]byte, er } func (n *leaf) prove(m *mpt, keys []byte, proof [][]byte) (node, trie.Object, error) { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() if n.hashValue != nil { if len(proof) != 1 || !bytes.Equal(proof[0], n.serialized) { @@ -232,6 +246,7 @@ func (n *leaf) prove(m *mpt, keys []byte, proof [][]byte) (node, trie.Object, er return n, nil, err } if changed { + lock.Migrate() n.value = value } return n, n.value, nil @@ -258,8 +273,8 @@ func (n *leaf) resolve(m *mpt, bd merkle.Builder) error { } func (n *leaf) compact() node { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() if n.state < stateFlushed { n.value.ClearCache() diff --git a/common/trie/ompt/mpt.go b/common/trie/ompt/mpt.go index ceadf8977..94cd0996a 100644 --- a/common/trie/ompt/mpt.go +++ b/common/trie/ompt/mpt.go @@ -40,17 +40,39 @@ type ( } mpt struct { mptBase - nibs []byte cache *cache.NodeCache root node - mutex sync.Mutex + mutex sync.RWMutex s *mptStatics } ) +var bytesPool sync.Pool = sync.Pool{ + New: func() interface{} { + return make([]byte, hashSize*2) + }, +} + +func allocNibbles(size int) []byte { + if size < 0 { + return nil + } + if size > hashSize { + return make([]byte, size*2) + } + bs := bytesPool.Get().([]byte) + return bs[0:size*2] +} + +func freeNibbles(nibs []byte) { + if cap(nibs) != hashSize*2 { + return + } + bytesPool.Put(nibs) +} + func bytesToNibs(k []byte) []byte { - ks := len(k) - nibs := make([]byte, ks*2) + nibs := allocNibbles(len(k)) for i, v := range k { nibs[i*2] = (v >> 4) & 0x0F @@ -64,19 +86,6 @@ func (mb *mptBase) Equal(mb2 *mptBase) bool { mb.objectType == mb2.objectType } -func (m *mpt) bytesToNibs(k []byte) []byte { - ks := len(k) - if cap(m.nibs) < ks*2 { - m.nibs = make([]byte, ks*2) - } - nibs := m.nibs[0 : ks*2] - - for i, v := range k { - nibs[i*2] = (v >> 4) & 0x0F - nibs[i*2+1] = v & 0x0F - } - return nibs -} func (m *mpt) get(n node, nibs []byte, depth int) (node, trie.Object, error) { if n == nil { @@ -148,19 +157,24 @@ func (m *mpt) realize(h []byte, nibs []byte) (node, error) { } func (m *mpt) Get(k []byte) (trie.Object, error) { - m.mutex.Lock() - defer m.mutex.Unlock() + lock := RLock(&m.mutex) + defer lock.Unlock() if logStatics { atomic.AddInt32(&m.s.get, 1) } - root, obj, err := m.get(m.root, m.bytesToNibs(k), 0) - m.root = root + nibs := bytesToNibs(k) + defer freeNibbles(nibs) + root, obj, err := m.get(m.root, nibs, 0) + if m.root != root { + lock.Migrate() + m.root = root + } return obj, err } func (m *mpt) Hash() []byte { - m.mutex.Lock() - defer m.mutex.Unlock() + m.mutex.RLock() + defer m.mutex.RUnlock() if m.root != nil { return m.root.getLink(true) } else { @@ -169,8 +183,8 @@ func (m *mpt) Hash() []byte { } func (m *mpt) Flush() error { - m.mutex.Lock() - defer m.mutex.Unlock() + m.mutex.RLock() + defer m.mutex.RUnlock() if m.root != nil { // Before flush node data to Database, We need to make sure that it // builds required data for dumping data. @@ -235,7 +249,9 @@ func (m *mpt) Set(k []byte, o trie.Object) (trie.Object, error) { if logStatics { atomic.AddInt32(&m.s.set, 1) } - root, _, old, err := m.set(m.root, m.bytesToNibs(k), 0, o) + nibs := bytesToNibs(k) + defer freeNibbles(nibs) + root, _, old, err := m.set(m.root, nibs, 0, o) m.root = root if debugDump && root != nil { root.dump() @@ -252,7 +268,9 @@ func (m *mpt) Delete(k []byte) (trie.Object, error) { if logStatics { atomic.AddInt32(&m.s.set, 1) } - root, dirty, old, err := m.delete(m.root, m.bytesToNibs(k), 0) + nibs := bytesToNibs(k) + defer freeNibbles(nibs) + root, dirty, old, err := m.delete(m.root, nibs, 0) if dirty { m.root = root if debugDump && root != nil { @@ -397,12 +415,13 @@ func (m *mpt) Iterator() trie.IteratorForObject { } func (m *mpt) Filter(prefix []byte) trie.IteratorForObject { - m.mutex.Lock() - defer m.mutex.Unlock() + lock := RLock(&m.mutex) + defer lock.Unlock() root := m.root if root != nil { - if n, err := root.realize(m); err == nil { + if n, err := root.realize(m); err == nil && n != root { + lock.Migrate() root = n m.root = n } @@ -435,7 +454,9 @@ func (m *mpt) GetProof(k []byte) [][]byte { proofs := [][]byte(nil) - root, proofs, err := m.root.getProof(m, m.bytesToNibs(k), proofs) + nibs := bytesToNibs(k) + defer freeNibbles(nibs) + root, proofs, err := m.root.getProof(m, nibs, proofs) if root != m.root { m.root = root } @@ -455,7 +476,9 @@ func (m *mpt) Prove(k []byte, proofs [][]byte) (trie.Object, error) { if m.root == nil { return nil, common.ErrIllegalArgument } - root, obj, err := m.root.prove(m, m.bytesToNibs(k), proofs) + nibs := bytesToNibs(k) + defer freeNibbles(nibs) + root, obj, err := m.root.prove(m, nibs, proofs) if root != m.root { m.root = root } diff --git a/common/trie/ompt/node.go b/common/trie/ompt/node.go index ee6aec738..91b7e0044 100644 --- a/common/trie/ompt/node.go +++ b/common/trie/ompt/node.go @@ -70,22 +70,27 @@ type nodeBase struct { hashValue []byte serialized []byte state nodeState - mutex sync.Mutex + mutex sync.RWMutex } func (n *nodeBase) hash() []byte { return n.hashValue } +func (n *nodeBase) rlock() AutoRWUnlock { + return RLock(&n.mutex) +} + func (n *nodeBase) getLink(n2 node, forceHash bool) []byte { - n.mutex.Lock() - defer n.mutex.Unlock() + lock := n.rlock() + defer lock.Unlock() if n.state < stateHashed { bytes, err := rlpEncode(n2) if err != nil { log.Panicln("FAIL to serialize", n, err) } + lock.Migrate() n.serialized = bytes if len(n.serialized) > hashSize || forceHash { n.hashValue = calcHash(n.serialized) @@ -115,7 +120,6 @@ func (n *nodeBase) flushBaseInLock(m *mpt, nibs []byte) error { } m.cache.Put(nibs, n.hashValue, n.serialized) } - n.state = stateFlushed return nil } diff --git a/consensus/bitarray.go b/consensus/bitarray.go index 736db4509..8cd223b1c 100644 --- a/consensus/bitarray.go +++ b/consensus/bitarray.go @@ -4,36 +4,45 @@ import ( "fmt" "math/bits" "math/rand" + + "github.com/icon-project/goloop/common/errors" ) type word = uint64 const wordBits = 64 -type bitArray struct { +type BitArray struct { NumBits int Words []word } -func (ba *bitArray) Len() int { +func (ba *BitArray) Verify() error { + if ba.NumBits > len(ba.Words)*wordBits { + return errors.Errorf("invalid BitArray NumBits=%d Words.len=%d", ba.NumBits, len(ba.Words)) + } + return nil +} + +func (ba *BitArray) Len() int { return ba.NumBits } -func (ba *bitArray) Set(idx int) { +func (ba *BitArray) Set(idx int) { if idx >= ba.NumBits { return } ba.Words[idx/wordBits] = ba.Words[idx/wordBits] | (1 << uint(idx%wordBits)) } -func (ba *bitArray) Unset(idx int) { +func (ba *BitArray) Unset(idx int) { if idx >= ba.NumBits { return } ba.Words[idx/wordBits] = ba.Words[idx/wordBits] &^ (1 << uint(idx%wordBits)) } -func (ba *bitArray) Put(idx int, v bool) { +func (ba *BitArray) Put(idx int, v bool) { if idx >= ba.NumBits { return } @@ -44,14 +53,14 @@ func (ba *bitArray) Put(idx int, v bool) { } } -func (ba *bitArray) Get(idx int) bool { +func (ba *BitArray) Get(idx int) bool { if idx >= ba.NumBits { return false } return ba.Words[idx/wordBits]&(1< ba2.NumBits { @@ -74,7 +83,7 @@ func (ba *bitArray) AssignAnd(ba2 *bitArray) { } } -func (ba *bitArray) PickRandom() int { +func (ba *BitArray) PickRandom() int { var count int for i := 0; i < len(ba.Words); i++ { count = count + bits.OnesCount64(ba.Words[i]) @@ -100,12 +109,12 @@ func (ba *bitArray) PickRandom() int { panic("PickRandom: internal error") } -func (ba bitArray) String() string { +func (ba BitArray) String() string { // TODO better form? return fmt.Sprintf("%x", ba.Words) } -func (ba *bitArray) Equal(ba2 *bitArray) bool { +func (ba *BitArray) Equal(ba2 *BitArray) bool { lba := len(ba.Words) if ba.NumBits != ba2.NumBits { return false @@ -118,12 +127,12 @@ func (ba *bitArray) Equal(ba2 *bitArray) bool { return true } -func (ba *bitArray) Copy() *bitArray { - ba2 := newBitArray(ba.NumBits) +func (ba *BitArray) Copy() *BitArray { + ba2 := NewBitArray(ba.NumBits) copy(ba2.Words, ba.Words) return ba2 } -func newBitArray(n int) *bitArray { - return &bitArray{n, make([]word, (n+wordBits-1)/wordBits)} +func NewBitArray(n int) *BitArray { + return &BitArray{n, make([]word, (n+wordBits-1)/wordBits)} } diff --git a/consensus/bitarray_test.go b/consensus/bitarray_test.go index e2d0fe38c..ac5eda925 100644 --- a/consensus/bitarray_test.go +++ b/consensus/bitarray_test.go @@ -24,7 +24,7 @@ func TestBitArray_PickRandom(t *testing.T) { {65, []int{64}, 2, 64}, } for _, c := range testCases { - ba := newBitArray(c.len) + ba := NewBitArray(c.len) for i := 0; i < len(c.ones); i++ { ba.Set(c.ones[i]) assert.True(ba.Get(c.ones[i])) @@ -51,13 +51,13 @@ func TestBitArray_PickRandom2(t *testing.T) { {500, []int{64, 77, 399}}, } for _, c := range testCases { - ba := newBitArray(c.len) + ba := NewBitArray(c.len) for i := 0; i < len(c.ones); i++ { ba.Set(c.ones[i]) assert.True(ba.Get(c.ones[i])) } baCopy := ba.Copy() - ba2 := newBitArray(c.len) + ba2 := NewBitArray(c.len) for i := 0; i < len(c.ones); i++ { v := ba.PickRandom() assert.True(ba.Get(v)) @@ -73,7 +73,7 @@ func TestBitArray_PickRandom2(t *testing.T) { func TestBitArray_Basics(t *testing.T) { assert := assert.New(t) const bits = 10 - ba := newBitArray(bits) + ba := NewBitArray(bits) assert.EqualValues(bits, ba.Len()) assert.EqualValues(false, ba.Get(2)) @@ -83,11 +83,11 @@ func TestBitArray_Basics(t *testing.T) { assert.EqualValues(false, ba.Get(2)) assert.EqualValues(false, ba.Get(11)) - ba2 := newBitArray(bits + 1) + ba2 := NewBitArray(bits + 1) ba2.AssignAnd(ba) assert.EqualValues(bits, ba2.Len()) - ba2 = newBitArray(bits + 1) + ba2 = NewBitArray(bits + 1) assert.False(ba.Equal(ba2)) ba2 = ba.Copy() diff --git a/consensus/commitvotelist.go b/consensus/commitvotelist.go index abdec4e5f..5648e2e18 100644 --- a/consensus/commitvotelist.go +++ b/consensus/commitvotelist.go @@ -222,6 +222,9 @@ func (vl *CommitVoteList) toVoteList( if err != nil { return nil, err } + if pf.ValidatorCount() != valLen { + return nil, errors.Errorf("bad validator count in proof validatorCount=%d pf.validatorCount=%d", valLen, pf.ValidatorCount()) + } for v := 0; v < pf.ValidatorCount(); v++ { var bys []byte pp := pf.ProofPartAt(v) diff --git a/consensus/consensus.go b/consensus/consensus.go index f024457b9..3984cbdad 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -1910,7 +1910,7 @@ func (cs *consensus) GetPrecommits(r int32) *VoteList { return cs.hvs.votesFor(r, VoteTypePrecommit).voteList() } -func (cs *consensus) GetVotes(r int32, prevotesMask *bitArray, precommitsMask *bitArray) *VoteList { +func (cs *consensus) GetVotes(r int32, prevotesMask *BitArray, precommitsMask *BitArray) *VoteList { return cs.hvs.getVoteListForMask(r, prevotesMask, precommitsMask) } diff --git a/consensus/consensus_test.go b/consensus/consensus_test.go index 5f33ddde9..e45f971cd 100644 --- a/consensus/consensus_test.go +++ b/consensus/consensus_test.go @@ -128,6 +128,8 @@ func TestConsensus_ClientBasics(t *testing.T) { var rsm consensus.RoundStateMessage rsm.Height = 10 rsm.Sync = true + rsm.PrevotesMask = consensus.NewBitArray(0) + rsm.PrecommitsMask = consensus.NewBitArray(0) csh.Unicast(consensus.ProtoRoundState, &rsm, nil) var brm fastsync.BlockRequest diff --git a/consensus/message.go b/consensus/message.go index e7d333dad..bf94a53f2 100644 --- a/consensus/message.go +++ b/consensus/message.go @@ -267,7 +267,7 @@ func (msg *VoteMessage) Verify() error { if err := msg._HR.verify(); err != nil { return err } - if msg.Type < VoteTypePrevote || msg.Type > numberOfVoteTypes { + if msg.Type != VoteTypePrevote && msg.Type != VoteTypePrecommit { return errors.New("bad field value") } if msg.Type == VoteTypePrevote && len(msg.NTSVoteBases) > 0 { @@ -394,9 +394,9 @@ func (msg *VoteMessage) RLPDecodeSelf(d codec.Decoder) error { type peerRoundState struct { _HR - PrevotesMask *bitArray - PrecommitsMask *bitArray - BlockPartsMask *bitArray + PrevotesMask *BitArray + PrecommitsMask *BitArray + BlockPartsMask *BitArray Sync bool } @@ -424,6 +424,20 @@ func (msg *RoundStateMessage) Verify() error { if err := msg.peerRoundState._HR.verify(); err != nil { return err } + if msg.PrevotesMask == nil || msg.PrecommitsMask == nil { + return errors.Errorf("invalid RoundStateMessage PrevotesMask=%v PRecommitMask=%v", msg.PrevotesMask, msg.PrecommitsMask) + } + if err := msg.PrevotesMask.Verify(); err != nil { + return err + } + if err := msg.PrecommitsMask.Verify(); err != nil { + return err + } + if msg.BlockPartsMask != nil { + if err := msg.BlockPartsMask.Verify(); err != nil { + return err + } + } return nil } @@ -443,7 +457,7 @@ func (msg *VoteListMessage) Verify() error { if msg.VoteList == nil { return errors.Errorf("nil VoteList") } - return nil + return msg.VoteList.Verify() } func (msg VoteListMessage) String() string { diff --git a/consensus/partset.go b/consensus/partset.go index 475544812..b55b53d71 100644 --- a/consensus/partset.go +++ b/consensus/partset.go @@ -27,7 +27,7 @@ type PartSet interface { IsComplete() bool NewReader() io.Reader AddPart(Part) error - GetMask() *bitArray + GetMask() *BitArray } type PartSetBuffer interface { @@ -98,7 +98,7 @@ type partSet struct { added int parts []*part tree trie.Immutable - ba *bitArray + ba *BitArray } func (ps *partSet) ID() *PartSetID { @@ -135,9 +135,9 @@ func (ps *partSet) IsComplete() bool { return ps.added == len(ps.parts) } -func (ps *partSet) GetMask() *bitArray { +func (ps *partSet) GetMask() *BitArray { if ps == nil { - return &bitArray{0, nil} + return &BitArray{0, nil} } return ps.ba } @@ -246,7 +246,7 @@ func (b *partSetBuffer) PartSet() PartSet { } b.ps.tree = ss } - b.ps.ba = newBitArray(b.ps.added) + b.ps.ba = NewBitArray(b.ps.added) b.ps.ba.Flip() return b.ps } @@ -259,7 +259,7 @@ func NewPartSetFromID(h *PartSetID) PartSet { return &partSet{ parts: make([]*part, h.Count), tree: trie_manager.NewImmutable(db.NewNullDB(), h.Hash), - ba: newBitArray(int(h.Count)), + ba: NewBitArray(int(h.Count)), } } diff --git a/consensus/syncer.go b/consensus/syncer.go index 1b584a66e..06e83e78e 100644 --- a/consensus/syncer.go +++ b/consensus/syncer.go @@ -25,7 +25,7 @@ type Engine interface { // pvMask.Get(i) == 0 and a set of precommits pc(i) where // pcMask.Get(i) == 0. For example, if the all bits for mask is 1, // no votes are returned. - GetVotes(r int32, pvMask *bitArray, pcMask *bitArray) *VoteList + GetVotes(r int32, pvMask *BitArray, pcMask *BitArray) *VoteList GetRoundState() *peerRoundState Height() int64 @@ -110,7 +110,7 @@ func (p *peer) doSync() (module.ProtocolInfo, Message) { if partSet == nil { return 0, nil } - p.BlockPartsMask = newBitArray(partSet.Parts()) + p.BlockPartsMask = NewBitArray(partSet.Parts()) p.log.Tracef("PC for commit %v\n", p.Height) return ProtoVoteList, msg } diff --git a/consensus/votelist.go b/consensus/votelist.go index 9bfe89977..949e60c60 100644 --- a/consensus/votelist.go +++ b/consensus/votelist.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/icon-project/goloop/common" + "github.com/icon-project/goloop/common/errors" ) type VoteItem struct { @@ -65,6 +66,22 @@ func (vl *VoteList) Get(i int) *VoteMessage { return msg } +func (vl *VoteList) Verify() error { + for i := range vl.VoteItems { + pi := int(vl.VoteItems[i].PrototypeIndex) + if pi < 0 || pi >= len(vl.Prototypes) { + return errors.Errorf("invalid prototype index VoteItemIndex=%d PrototypeIndex=%d Prototypes.len=%d", i, vl.VoteItems[i].PrototypeIndex, len(vl.Prototypes)) + } + } + for i := 0; i < vl.Len(); i++ { + v := vl.Get(i) + if err := v.Verify(); err != nil { + return err + } + } + return nil +} + func NewVoteList() *VoteList { return &VoteList{} } diff --git a/consensus/voteset.go b/consensus/voteset.go index a3cf481f1..8350bbcea 100644 --- a/consensus/voteset.go +++ b/consensus/voteset.go @@ -21,7 +21,7 @@ type counter struct { type voteSet struct { msgs []*VoteMessage maxIndex int - mask *bitArray + mask *BitArray round int32 counters []counter @@ -186,7 +186,7 @@ func (vs *voteSet) getRoundEvidences(minRound int32, nid []byte) *VoteList { } // shall not modify returned array. invalidated if a vote is added. -func (vs *voteSet) getMask() *bitArray { +func (vs *voteSet) getMask() *BitArray { return vs.mask } @@ -210,7 +210,7 @@ func newVoteSet(nValidators int) *voteSet { return &voteSet{ msgs: make([]*VoteMessage, nValidators), maxIndex: -1, - mask: newBitArray(nValidators), + mask: NewBitArray(nValidators), round: -1, } } @@ -240,7 +240,7 @@ func (hvs *heightVoteSet) reset(nValidators int) { hvs._votes = make(map[int32][numberOfVoteTypes]*voteSet) } -func (hvs *heightVoteSet) getVoteListForMask(round int32, prevotesMask *bitArray, precommitsMask *bitArray) *VoteList { +func (hvs *heightVoteSet) getVoteListForMask(round int32, prevotesMask *BitArray, precommitsMask *BitArray) *VoteList { rvl := NewVoteList() prevotes := hvs.votesFor(round, VoteTypePrevote) for i, msg := range prevotes.msgs { diff --git a/service/eeproxy/enginefactory.go b/service/eeproxy/enginefactory.go index dc00db094..17369429e 100644 --- a/service/eeproxy/enginefactory.go +++ b/service/eeproxy/enginefactory.go @@ -7,6 +7,9 @@ import ( func AllocEngines(l log.Logger, names ...string) ([]Engine, error) { l.Infof("Allocate Engines:%s", names) + if len(names) == 1 && names[0] == "none" { + return make([]Engine,0), nil + } engines := make([]Engine, len(names)) for i, name := range names { switch name { diff --git a/service/state/worldvirtualstate.go b/service/state/worldvirtualstate.go index c3f8df919..408bcca46 100644 --- a/service/state/worldvirtualstate.go +++ b/service/state/worldvirtualstate.go @@ -44,6 +44,7 @@ type worldVirtualContext struct { real WorldState lastAccountLocker map[string]*worldVirtualState lastWorldLocker *worldVirtualState + roAccounts map[string]AccountState } func (wvc *worldVirtualContext) getLocker(id string, parent *worldVirtualState) *worldVirtualState { @@ -349,6 +350,19 @@ func (wvs *worldVirtualState) GetFuture(reqs []LockRequest) WorldVirtualState { return nwvs } +func (wvs *worldVirtualState) getROAccountState(id []byte) AccountState { + if wvs.roAccounts==nil { + wvs.roAccounts = make(map[string]AccountState) + } else { + if as, ok := wvs.roAccounts[string(id)]; ok { + return as + } + } + as := newAccountROState(wvs.Database(), wvs.real.GetAccountSnapshot(id)) + wvs.roAccounts[string(id)] = as + return as +} + func applyLockRequests(wvs *worldVirtualState, reqs []LockRequest) { for _, req := range reqs { if req.Lock != AccountReadLock && req.Lock != AccountWriteLock { @@ -401,8 +415,7 @@ func applyLockRequests(wvs *worldVirtualState, reqs []LockRequest) { if las.lock == AccountWriteLock { las.state = wvs.real.GetAccountState(idBytes) } else { - las.state = newAccountROState(wvs.Database(), - wvs.real.GetAccountSnapshot(idBytes)) + las.state = wvs.getROAccountState(idBytes) } } if las.lock == AccountWriteLock { diff --git a/service/trace/logger.go b/service/trace/logger.go index b209a670e..985d208ba 100644 --- a/service/trace/logger.go +++ b/service/trace/logger.go @@ -18,28 +18,25 @@ type Logger struct { } func (l *Logger) TraceMode() module.TraceMode { - if l.cb != nil { - return l.traceMode - } - return module.TraceModeNone -} - -func (l *Logger) onLog(lv module.TraceLevel, msg string) { - if l.TraceMode() == module.TraceModeInvoke { - l.cb.OnLog(lv, msg) - } + return l.traceMode } func (l *Logger) TLog(lv module.TraceLevel, a ...interface{}) { - l.onLog(lv, l.prefix+fmt.Sprint(a...)) + if l.traceMode == module.TraceModeInvoke { + l.cb.OnLog(lv, l.prefix+fmt.Sprint(a...)) + } } func (l *Logger) TLogln(lv module.TraceLevel, a ...interface{}) { - l.onLog(lv, l.prefix+fmt.Sprint(a...)) + if l.traceMode == module.TraceModeInvoke { + l.cb.OnLog(lv, l.prefix+fmt.Sprint(a...)) + } } func (l *Logger) TLogf(lv module.TraceLevel, f string, a ...interface{}) { - l.onLog(lv, l.prefix+fmt.Sprintf(f, a...)) + if l.traceMode == module.TraceModeInvoke { + l.cb.OnLog(lv, l.prefix+fmt.Sprintf(f, a...)) + } } func (l *Logger) TDebug(a ...interface{}) { @@ -238,6 +235,8 @@ func NewLogger(l log.Logger, ti *module.TraceInfo) *Logger { tlog.traceMode = ti.TraceMode tlog.traceBlock = ti.TraceBlock tlog.cb = ti.Callback + } else { + tlog.traceMode = module.TraceModeNone } return tlog } diff --git a/service/transaction/transactionlist.go b/service/transaction/transactionlist.go index 038ef8bd6..1eea6423c 100644 --- a/service/transaction/transactionlist.go +++ b/service/transaction/transactionlist.go @@ -16,7 +16,8 @@ import ( ) type transactionList struct { - trie trie.ImmutableForObject + trie trie.ImmutableForObject + writer db.Writer } func intToKey(i int) []byte { @@ -65,6 +66,9 @@ func (l *transactionList) Iterator() module.TransactionIterator { } func (l *transactionList) Hash() []byte { + if l.writer != nil { + defer l.writer.Prepare() + } return l.trie.Hash() } @@ -73,6 +77,9 @@ func (l *transactionList) Equal(t module.TransactionList) bool { } func (l *transactionList) Flush() error { + if l.writer != nil { + return l.writer.Flush() + } if ss, ok := l.trie.(trie.SnapshotForObject); ok { return ss.Flush() } @@ -81,15 +88,24 @@ func (l *transactionList) Flush() error { func NewTransactionListFromHash(d db.Database, h []byte) module.TransactionList { t := trie_manager.NewImmutableForObject(d, h, TransactionType) - return &transactionList{t} + return &transactionList{t, nil} } func NewTransactionListFromSlice(dbase db.Database, list []module.Transaction) module.TransactionList { - mt := trie_manager.NewMutableForObject(dbase, nil, TransactionType) + if len(list) == 0 { + return NewTransactionListFromHash(dbase, nil) + } + writer := db.NewWriter(dbase) + mt := trie_manager.NewMutableForObject(writer.Database(), nil, TransactionType) for idx, tx := range list { - mt.Set(intToKey(idx), tx.(trie.Object)) + _, err := mt.Set(intToKey(idx), tx.(trie.Object)) + if err != nil { + panic(err) + } } - return &transactionList{mt.GetSnapshot()} + snapshot := mt.GetSnapshot() + writer.Add(snapshot) + return &transactionList{snapshot, writer} } func NewTransactionListWithBuilder(builder merkle.Builder, h []byte) module.TransactionList { @@ -97,5 +113,5 @@ func NewTransactionListWithBuilder(builder merkle.Builder, h []byte) module.Tran snapshot := trie_manager.NewImmutableForObject(d, h, TransactionType) snapshot.Resolve(builder) // log.Printf("NewTransactionListWithBuilder: hash=%x size=%d", h, builder.UnresolvedCount()) - return &transactionList{snapshot} + return &transactionList{snapshot, nil} } diff --git a/service/transition.go b/service/transition.go index 131ff4b3d..651d94872 100644 --- a/service/transition.go +++ b/service/transition.go @@ -132,6 +132,7 @@ type transition struct { mutex sync.Mutex result []byte + receiptWriter db.Writer worldSnapshot state.WorldSnapshot patchReceipts module.ReceiptList normalReceipts module.ReceiptList @@ -140,6 +141,7 @@ type transition struct { transactionCount int executeDuration time.Duration + txFlushDuration time.Duration syncer ssync.Syncer @@ -702,8 +704,11 @@ func (t *transition) doExecute(alreadyValidated bool) { } } } - t.patchReceipts = txresult.NewReceiptListFromSlice(t.db, patchReceipts) - t.normalReceipts = txresult.NewReceiptListFromSlice(t.db, normalReceipts) + t.receiptWriter = db.NewWriter(t.db) + t.patchReceipts = txresult.NewReceiptListFromSlice(t.receiptWriter.Database(), patchReceipts) + t.normalReceipts = txresult.NewReceiptListFromSlice(t.receiptWriter.Database(), normalReceipts) + t.receiptWriter.Add(t.patchReceipts) + t.receiptWriter.Add(t.normalReceipts) // save gathered fee to treasury tr := ctx.GetAccountState(ctx.Treasury().ID()) @@ -746,6 +751,8 @@ func (t *transition) doExecute(alreadyValidated bool) { } t.result = tresult.Bytes() + t.receiptWriter.Prepare() + t.reportExecution(nil) } @@ -803,9 +810,13 @@ func (t *transition) executeTxs(l module.TransactionList, ctx contract.Context, } func (t *transition) finalizeNormalTransaction() error { + startTS := time.Now(); if err := t.commitTXIDs(module.TransactionGroupNormal); err != nil { return err } + defer func() { + t.txFlushDuration = time.Since(startTS) + }() return t.normalTransactions.Flush() } @@ -830,11 +841,17 @@ func (t *transition) finalizeResult(noFlush bool, keepParent bool) error { return err } worldTS = time.Now() - if err := t.patchReceipts.Flush(); err != nil { - return err - } - if err := t.normalReceipts.Flush(); err != nil { - return err + if t.receiptWriter != nil { + if err := t.receiptWriter.Flush(); err != nil { + return err + } + } else { + if err := t.patchReceipts.Flush(); err != nil { + return err + } + if err := t.normalReceipts.Flush(); err != nil { + return err + } } } } @@ -844,7 +861,7 @@ func (t *transition) finalizeResult(noFlush bool, keepParent bool) error { finalTS := time.Now() t.onWorldFinalize(t.worldSnapshot) - t.chain.Regulator().OnTxExecution(t.transactionCount, t.executeDuration, finalTS.Sub(startTS)) + t.chain.Regulator().OnTxExecution(t.transactionCount, t.executeDuration, t.txFlushDuration+finalTS.Sub(startTS)) t.log.Infof("finalizeResult() total=%s world=%s receipts=%s", finalTS.Sub(startTS), worldTS.Sub(startTS), finalTS.Sub(worldTS)) return nil