cosmos / iavl

Merkleized IAVL+ Tree implementation in Go
Apache License 2.0
409 stars 256 forks source link

PANIC: in node.go func (node *Node) String() string #940

Open quaisx opened 2 months ago

quaisx commented 2 months ago

when calling Node::String with the following node values []interface {} len: 8, cap: 8, ["\x02","",nil,[]uint8 len: 0, cap: 0, nil,[]uint8 len: 0, cap: 0, nil,3,2,""] results in panic

func (node *Node) String() string {
    child := ""
    if node.leftNode != nil && node.leftNode.nodeKey != nil {
        child += fmt.Sprintf("{left %v}", node.leftNode.nodeKey)
    }
    if node.rightNode != nil && node.rightNode.nodeKey != nil {
        child += fmt.Sprintf("{right %v}", node.rightNode.nodeKey)
    }
    return fmt.Sprintf("Node{%s:%s@ %v:%v-%v %d-%d}#%s\n",
        color.ColoredBytes(node.key, color.Green, color.Blue),
        color.ColoredBytes(node.value, color.Cyan, color.Blue),
        node.nodeKey, node.leftNodeKey, node.rightNodeKey,
        node.size, node.subtreeHeight, child)
}

with the following node values []interface {} len: 8, cap: 8, ["\x02","",nil,[]uint8 len: 0, cap: 0, nil,[]uint8 len: 0, cap: 0, nil,3,2,""]

When processing the 3rd arg (arg).(data) *iavl.NodeKey nil in /usr/local/go/src/fmt/print.go

func (p *pp) printArg(arg any, verb rune) {
    p.arg = arg
    p.value = reflect.Value{}

    if arg == nil {
        switch verb {
        case 'T', 'v':
            p.fmt.padString(nilAngleString)
        default:
            p.badVerb(verb)
        }
        return
    }

    // Special processing considerations.
    // %T (the value's type) and %p (its address) are special; we always do them first.
    switch verb {
    case 'T':
        p.fmt.fmtS(reflect.TypeOf(arg).String())
        return
    case 'p':
        p.fmtPointer(reflect.ValueOf(arg), 'p')
        return
    }

    // Some types can be done without reflection.
    switch f := arg.(type) {
    ...
    default:
        // If the type is not simple, it might have methods.
    [here]=>    if !p.handleMethods(verb) {
            // Need to use reflection, since the type had no
            // interface methods that could be used for formatting.
            p.printValue(reflect.ValueOf(f), verb, 0)
        }
    }
}

inside p.handeMethods(verb) the call to p.fmtString(v.String(), verb) on v=nil

Suggestion: change the format string to fmt.Sprintf("Node{%s:%s@ %#v:%#v-%#v %d-%d}#%s\n"